Skip to content

Instantly share code, notes, and snippets.

@perusio
Forked from shrikeh/csrf-lua.conf
Last active December 14, 2015 02:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save perusio/5017911 to your computer and use it in GitHub Desktop.
Save perusio/5017911 to your computer and use it in GitHub Desktop.
server {
listen 80;
root /root/to/your/docroot;
proxy_redirect off;
proxy_intercept_errors on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Request-URI $request_uri;
proxy_set_header X-Backend example;
proxy_pass_header Set-Cookie;
location ~ \..*/.*\.php$ {
return 403;
}
location / {
# This is cool because no php is touched for static content
expires max;
try_files $uri @backend;
}
location /validate-csrf {
# Only accessible as a subrequest
internal;
content_by_lua
'
if ngx.var.cookie_csrf then
-- user has a CSRF cookie, validate it
local redis = require "redis"
local client = redis.connect("127.0.0.1", 6379)
local csrf_cookie = "csrf_" .. ngx.var.cookie_csrf
local value = client:get(csrf_cookie)
if value then
ngx.say(value) -- useful for testing
return ngx.exit(ngx.HTTP_OK)
end
end
-- no cookie or csrf provided was not found
return ngx.exit(ngx.HTTP_NOT_FOUND)
';
}
location @backend {
# You can't set variables in nginx dynamically, so set this up as empty first
set $csrf_validate "";
access_by_lua
'
if ngx.req.get_method() == "POST" then
-- set up forbidden as default
ngx.var.csrf_validate = ngx.HTTP_FORBIDDEN
if ngx.var.cookie_csrf then
local res = ngx.location.capture("/validate-csrf")
if ngx.HTTP_OK == res.status then
ngx.req.read_body()
local args = ngx.req.get_post_args()
local posted_token = tostring(args["csrf"])
if posted_token == res.body then
ngx.var.csrf_validate = ngx.HTTP_OK
end
end
end
end
';
# Pass the result as a header to the backend
proxy_set_header X-Csrf-Valid $csrf_validate;
proxy_pass http://127.0.0.1:6081;
# Now filter the response from the backend in as lightweight way as possible
set $csrf_form_token "";
set $csrf_cookie_token "";
header_filter_by_lua
'
if ngx.var.upstream_http_x_csrf_tokenize then
-- the backend requested a CSRF token be set
local csrf_cookie_token = nil
if ngx.var.cookie_csrf then
-- they have a cookie, just re-use it
local csrf_cookie_token = ngx.var.cookie_csrf
end
local resty_random = require "resty.random"
local str = require "resty.string"
if not csrf_cookie_token then
-- no valid csrf cookie found, let us make one
local cookie_random = resty_random.bytes(16,true)
while cookie_random == nil do
-- attempt to generate 16 bytes of
-- cryptographically strong (enough) random data
cookie_random = resty_random.bytes(16,true)
end
ngx.var.csrf_cookie_token = str.to_hex(cookie_random)
end
-- we are about to mess around with the content of the page
-- so we need to clear this as it will be wrong
ngx.header.Content_Length = ""
-- set the Cookie for the CSRF token
ngx.header.Set_Cookie = "csrf=" .. ngx.var.csrf_cookie_token
ngx.header.tokenize = ngx.var.upstream_http_x_csrf_tokenize
-- now generate one for the form token
while form_random == nil do
form_random = resty_random.bytes(16,true)
end
ngx.var.csrf_form_token = str.to_hex(form_random)
local redis = require "redis"
local client = redis.connect("127.0.0.1", 6379)
client:set("csrf_" .. ngx.var.csrf_cookie_token, ngx.var.csrf_form_token)
end
';
# Parse the body for csrf placeholder(s) and replace them with a hash
body_filter_by_lua
'
local csrf_form_token = ngx.var.csrf_form_token or ""
if ngx.var.csrf_form_token then
local placeholder = ngx.var.upstream_http_x_csrf_tokenize or ""
ngx.arg[1] = ngx.re.gsub(ngx.arg[1], placeholder, ngx.var.csrf_form_token)
end
';
}
@shrikeh
Copy link

shrikeh commented Feb 28, 2014

I'm amused that I was recently in awe of your Drupal nginx conf (I'm mentioning it in a talk I'm giving tomorrow) and when digging out my example of this and saw you had forked it a year ago!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment