Skip to content
This repository has been archived by the owner on Jul 5, 2020. It is now read-only.

Commit

Permalink
Implement CSRF protection
Browse files Browse the repository at this point in the history
  • Loading branch information
mshibuya committed Dec 18, 2018
1 parent 70ed3c8 commit 1daf359
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 3 deletions.
7 changes: 7 additions & 0 deletions app/controllers/application_controller.rb
@@ -1,2 +1,9 @@
class ApplicationController < ActionController::API
include ActionController::RequestForgeryProtection
protect_from_forgery with: :exception
after_action :set_csrf_token_header

def set_csrf_token_header
response.set_header("X-CSRF-Token", form_authenticity_token)
end
end
11 changes: 11 additions & 0 deletions app/controllers/top_controller.rb
@@ -0,0 +1,11 @@
class TopController < ApplicationController
before_action :authenticate_user!

def get
head :no_content
end

def post
render json: {text: params[:top][:text]}
end
end
5 changes: 5 additions & 0 deletions app/controllers/users/sessions_controller.rb
@@ -0,0 +1,5 @@
# frozen_string_literal: true

class Users::SessionsController < Devise::SessionsController
skip_before_action :verify_authenticity_token, only: :create
end
3 changes: 3 additions & 0 deletions config/application.rb
Expand Up @@ -31,5 +31,8 @@ class Application < Rails::Application
# Middleware like session, flash, cookies can be added back manually.
# Skip views, helpers and assets when generating a new resource.
config.api_only = true

config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore, {:key=>"_csrf_protection_example_session"}
end
end
2 changes: 1 addition & 1 deletion config/initializers/devise.rb
Expand Up @@ -248,7 +248,7 @@
# should add them to the navigational formats lists.
#
# The "*/*" below is required to match Internet Explorer requests.
# config.navigational_formats = ['*/*', :html]
config.navigational_formats = [:json]

# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :delete
Expand Down
5 changes: 3 additions & 2 deletions config/routes.rb
@@ -1,4 +1,5 @@
Rails.application.routes.draw do
devise_for :users
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
devise_for :users, controllers: {sessions: 'users/sessions'}
get '/get', to: 'top#get'
post '/post', to: 'top#post'
end
1 change: 1 addition & 0 deletions db/seeds.rb
Expand Up @@ -5,3 +5,4 @@
#
# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
# Character.create(name: 'Luke', movie: movies.first)
User.create(email: 'username@example.com', password: 'password')
79 changes: 79 additions & 0 deletions public/index.html
@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<title>CSRF protection demo</title>
<script src="https://unpkg.com/vue@2.5.21/dist/vue.js"></script>
<script src="https://unpkg.com/axios@0.18.0/dist/axios.js"></script>
</head>
<body>
<div id="app">
<div><button v-on:click="login">Login</button></div>

<div><button v-on:click="get">GET</button></div>

<div>
<form v-on:submit.prevent="post">
<input type="text" v-model="text" />
<button type="submit">POST</button>
</form>
<p>Response: {{ response }}</p>
</div>

<div>
<p>CSRF Token: {{ csrfToken }}</p>
<button type="submit" v-on:click="clearToken">clear Token</button>
</div>
</div>

<script>
var http = axios.create();
http.interceptors.response.use(function (response) {
var token = response.headers['x-csrf-token'];
if (token) {
// save token in localStorage for later use
app.csrfToken = token;
window.localStorage.setItem('csrf-token', token);
}
return response;
}, function (error) {});
http.interceptors.request.use(function (config) {
var token = window.localStorage.getItem('csrf-token');
config.headers['X-CSRF-Token'] = token;
return config;
}, function (error) {});

var app = new Vue({
el: '#app',
data: {
csrfToken: window.localStorage.getItem('csrf-token'),
text: '',
response: '-'
},
methods: {
login: function () {
http.post('/users/sign_in', {
user: {
email: 'username@example.com',
password: 'password'
}
}, {});
},
get: function () {
http.get('/get');
},
post: function () {
http.post('/post', {
top: {text: this.text}
}, {}).then(function (response) {
app.response = response.data.text;
});
},
clearToken: function () {
window.localStorage.removeItem('csrf-token');
this.csrfToken = '-';
}
}
});
</script>
</body>
</html>

0 comments on commit 1daf359

Please sign in to comment.