CSRF protection in front-end frameworks
Basic Principles of CSRF Attacks
CSRF (Cross-Site Request Forgery) is a common web security threat. When an attacker induces a user to visit a malicious website, the website sends requests to the target website where the user is authenticated, leveraging the user's logged-in status to perform unauthorized actions. This attack succeeds because the browser automatically includes authentication information such as cookies from the target website.
Typical CSRF attack flow:
- The user logs into a banking website, and authentication information is stored in cookies.
- The user visits a malicious website.
- The malicious website contains a request to transfer money from the bank.
- The browser automatically sends the request with the banking website's cookies.
- The bank server treats this as a legitimate request and executes the operation.
<!-- Attack code in the malicious website -->
<img src="https://bank.com/transfer?to=attacker&amount=10000" width="0" height="0">
CSRF Protection Mechanisms in Frontend Frameworks
Modern frontend frameworks provide various built-in or recommended CSRF protection solutions. These mechanisms primarily focus on verifying request origins and adding random tokens.
1. Same-Origin Policy and CORS
The browser's same-origin policy is the first line of defense against CSRF. Frontend frameworks strictly adhere to CORS specifications when handling cross-origin requests:
// Axios configuration example
axios.defaults.withCredentials = true;
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
Key configuration items:
withCredentials
: Controls whether credentials are sent.Access-Control-Allow-Origin
: Specifies allowed origins.Access-Control-Allow-Credentials
: Determines whether cookies can be sent.
2. CSRF Token Mechanism
The mainstream framework-recommended protection involves adding CSRF tokens to forms or requests:
React Implementation Example
// Server generates and injects the token
const csrfToken = generateToken();
// Frontend component
function TransferForm() {
return (
<form action="/transfer" method="POST">
<input type="hidden" name="_csrf" value={csrfToken} />
{/* Other form fields */}
</form>
);
}
Vue Implementation Example
// Global interceptor setup
axios.interceptors.request.use(config => {
config.headers['X-CSRF-TOKEN'] = getCSRFToken();
return config;
});
3. Double Cookie Verification
Modern frameworks support enhanced protection through double cookies:
// Set HttpOnly authentication cookie
document.cookie = `auth_token=${token}; HttpOnly; Secure`;
// Also set a readable CSRF token
document.cookie = `csrf_token=${csrfToken}; SameSite=Strict`;
Framework-Specific Implementation Solutions
Angular's CSRF Protection
Angular has built-in XSRF protection, which automatically reads the XSRF-TOKEN
from cookies and adds it to request headers:
// Server sets the cookie
res.cookie('XSRF-TOKEN', token, { secure: true });
// Angular handles it automatically
this.http.post('/api/transfer', data).subscribe();
React's Protection Practices
React applications often combine Redux to manage CSRF state:
// Redux store initialization
const store = createStore(reducer, {
csrf: {
token: window.initialData.csrfToken,
timestamp: Date.now()
}
});
// Higher-order component encapsulation
function withCSRF(Component) {
return function WrappedComponent(props) {
const csrf = useSelector(state => state.csrf);
return <Component {...props} csrf={csrf} />;
}
}
Vue's Solutions
The Vue ecosystem typically uses vue-resource or axios plugins:
// Vue plugin implementation
const CSRFPlugin = {
install(Vue) {
Vue.prototype.$csrf = {
getToken() {
return localStorage.getItem('csrfToken');
},
refresh() {
return axios.get('/csrf-token').then(res => {
localStorage.setItem('csrfToken', res.data.token);
});
}
};
}
};
Advanced Protection Strategies
1. Request Header Verification
In addition to tokens, specific request headers can be validated:
// Custom request header verification
app.use((req, res, next) => {
if (req.get('X-Request-Source') !== 'web-app') {
return res.status(403).send('Invalid request source');
}
next();
});
2. Behavioral Verification
Add secondary verification for critical operations:
// React confirmation dialog
function ConfirmTransfer({ amount, recipient }) {
const [confirmed, setConfirmed] = useState(false);
return (
<div>
{!confirmed && (
<dialog open>
<p>Confirm transferring {amount} to {recipient}?</p>
<button onClick={() => setConfirmed(true)}>Confirm</button>
</dialog>
)}
{confirmed && <TransferForm />}
</div>
);
}
3. SameSite Cookie Attribute
Modern browsers support the SameSite attribute:
// Strict mode cookie setting
Set-Cookie: sessionid=xxxx; SameSite=Strict; Secure
Practical Considerations
- Token Storage: Avoid storing CSRF tokens in localStorage; use HttpOnly cookies instead.
- Token Refresh: Refresh CSRF tokens before critical operations.
- Error Handling: Handle 403 status codes uniformly and guide users to re-authenticate.
- Logging: Record CSRF validation failures for security analysis.
// Error handling middleware
app.use((err, req, res, next) => {
if (err.code === 'EBADCSRFTOKEN') {
securityLogger.warn(`CSRF validation failed: ${req.ip}`);
return res.status(403).render('error/csrf');
}
next(err);
});
Balancing Performance and Security
CSRF protection can impact application performance; optimize appropriately:
- Token Caching: Exempt static resource requests from CSRF validation.
- Domain Whitelisting: Configure trusted domains to reduce validation overhead.
- Lazy Validation: Delay validation for non-sensitive operations.
# Nginx configuration example
location ~ ^/api/secure/ {
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
}
location ~ ^/static/ {
expires 1y;
add_header Cache-Control "public";
}
Testing and Validation
Comprehensive testing ensures protection effectiveness:
// Jest test cases
describe('CSRF Protection', () => {
test('Should reject POST requests without CSRF header', async () => {
const res = await request(app)
.post('/transfer')
.expect(403);
});
test('Should accept requests with valid CSRF header', async () => {
const token = await getCSRFToken();
const res = await request(app)
.post('/transfer')
.set('X-CSRF-TOKEN', token)
.expect(200);
});
});
Synergy with Other Security Measures
CSRF protection should work with other security mechanisms:
- CSP Policy: Restrict external resource loading.
- XSS Protection: Prevent token theft.
- Rate Limiting: Block brute-force attacks.
<!-- CSP policy example -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline'">
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:双重提交 Cookie 方案
下一篇:自动化检测与工具推荐