Security
Security considerations and best practices for the Gradescope Submitter.
Credential Protection
Server-Side Only
Gradescope credentials (TOKEN and COOKIE) are:
- Stored only in
.envfile on server - Never sent to the browser
- Never logged in production
- Never committed to version control
Environment Variables
env
# These stay on the server
TOKEN=your-gradescope-authenticity-token
COOKIE=your-gradescope-session-cookie
SECRET=your-signing-secret.gitignore
The .env file is excluded from version control:
gitignore
.env
*.pem
keys/LTI Security
JWT Validation
All LTI launches are validated:
- Signature verification — JWT signed by D2L
- Issuer check — Must match D2L's issuer URL
- Audience check — Must match our client ID
- Expiration check — Token must not be expired
- Nonce validation — Prevents replay attacks
State Tokens
State tokens in the auth flow:
- Signed with HMAC-SHA256
- Include expiration timestamp
- Single-use (should be invalidated)
javascript
const state = jwt.sign(
{ email, asgId, exp: Date.now() + 300000 },
process.env.SECRET
);HTTPS Requirements
Why HTTPS?
- D2L requirement — Iframes must load over HTTPS
- Data protection — Encrypts code and credentials in transit
- Browser policies — Modern browsers block mixed content
Certificate Management
- Use Let's Encrypt for free certificates
- Auto-renew before expiration
- Store certificates securely
Input Validation
Server-Side Validation
All inputs are validated before processing:
javascript
app.post('/submit', (req, res) => {
const { code, asg_id } = req.body;
if (!code || typeof code !== 'string') {
return res.status(400).json({ error: 'Invalid code' });
}
if (!asg_id || !/^\d+$/.test(asg_id)) {
return res.status(400).json({ error: 'Invalid assignment ID' });
}
// ... proceed with validated data
});Sanitization
- Assignment IDs: Must be numeric
- File names: Restricted to safe characters
- Code: Passed as-is to Gradescope (their autograder handles execution)
Code Execution Safety
Browser Sandbox
Pyodide runs in the browser's WebAssembly sandbox:
- No file system access — Can't read/write local files
- No network access — Can't make HTTP requests
- No system calls — Can't execute shell commands
- Memory limited — Browser enforces limits
What Students Can't Do
python
# These will fail in Pyodide:
import os
os.system('rm -rf /') # No system access
import requests
requests.get('http://evil.com') # No network
open('/etc/passwd', 'r') # No file systemSession Security
In-Memory Sessions
Student sessions are stored in memory:
- Lost on server restart
- No persistent storage vulnerabilities
- Automatic cleanup
Session Isolation
Each student's credentials are separate:
javascript
const sessions = new Map();
function storeSession(userId, credentials) {
sessions.set(userId, credentials);
}
function getSession(userId) {
return sessions.get(userId);
}Rate Limiting
Recommended Implementation
javascript
const rateLimit = require('express-rate-limit');
const submitLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 10, // 10 submissions per minute
message: { error: 'Too many submissions, please wait' }
});
app.use('/submit', submitLimiter);Why Rate Limit?
- Prevent abuse
- Protect Gradescope API
- Ensure fair access
Error Handling
Don't Expose Internals
javascript
// Bad - exposes internal details
res.status(500).json({ error: err.stack });
// Good - generic message
res.status(500).json({ error: 'Submission failed' });Log Securely
javascript
// Don't log credentials
console.log('Submitting for user:', email);
// NOT: console.log('Cookie:', cookie);Security Checklist
Deployment
- [ ] HTTPS enabled
- [ ]
.envnot in version control - [ ] Strong
SECRETvalue - [ ] Credentials rotated regularly
- [ ] Rate limiting enabled
- [ ] Error messages don't expose internals
Code Review
- [ ] No hardcoded credentials
- [ ] Input validation on all endpoints
- [ ] Proper error handling
- [ ] No sensitive data in logs
Monitoring
- [ ] Log failed authentication attempts
- [ ] Monitor for unusual submission patterns
- [ ] Alert on repeated errors
Vulnerability Reporting
If you discover a security vulnerability:
- Do not open a public issue
- Email the maintainers directly
- Provide details and reproduction steps
- Allow time for a fix before disclosure