├── LICENSE ├── README.md └── application ├── config └── hooks.php └── hooks └── csrf.php /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by Linkworks 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeIgniter CSRF Library 2 | 3 | This library automatically protects all your forms against Cross-Site Request Forgery attacks, more 4 | commonly refered to as CSRF. 5 | 6 | Just put the `application/hooks/csrf.php` in your hooks folder, and the `application/config/hooks.php` 7 | file in your hooks.php config file (or add the content in case you're already using a hook), and you're done! 8 | 9 | The only caveat is that ajax POST requests will get rejected once the library is 10 | installed. To fix this you need to include the CSRF token in your requests. This 11 | token is injected into the `` of your files automatically, so all you need 12 | to do is get it using any javascript framework and include it in the request. Check 13 | the two lines just befor the ending your `` (``) to figure it out. 14 | 15 | ## License 16 | 17 | License is the MIT license. This project belongs to [linkigniter](https://github.com/linkworks/linkigniter). 18 | -------------------------------------------------------------------------------- /application/config/hooks.php: -------------------------------------------------------------------------------- 1 | 'CSRF_Protection', 24 | 'function' => 'validate_tokens', 25 | 'filename' => 'csrf.php', 26 | 'filepath' => 'hooks' 27 | ); 28 | 29 | // Generates the token (MUST HAPPEN AFTER THE VALIDATION HAS BEEN MADE, BUT BEFORE THE CONTROLLER 30 | // IS EXECUTED, OTHERWISE USER HAS NO ACCESS TO A VALID TOKEN FOR CUSTOM FORMS). 31 | $hook['post_controller_constructor'][] = array( // Mind the "[]", this is not the only post_controller_constructor hook 32 | 'class' => 'CSRF_Protection', 33 | 'function' => 'generate_token', 34 | 'filename' => 'csrf.php', 35 | 'filepath' => 'hooks' 36 | ); 37 | 38 | // This injects tokens on all forms 39 | $hook['display_override'] = array( 40 | 'class' => 'CSRF_Protection', 41 | 'function' => 'inject_tokens', 42 | 'filename' => 'csrf.php', 43 | 'filepath' => 'hooks' 44 | ); 45 | 46 | /* End of file hooks.php */ 47 | /* Location: ./system/application/config/hooks.php */ -------------------------------------------------------------------------------- /application/hooks/csrf.php: -------------------------------------------------------------------------------- 1 | CI =& get_instance(); 33 | } 34 | 35 | // ----------------------------------------------------------------------------------- 36 | 37 | /** 38 | * Generates a CSRF token and stores it on session. Only one token per session is generated. 39 | * This must be tied to a post-controller hook, and before the hook 40 | * that calls the inject_tokens method(). 41 | * 42 | * @return void 43 | * @author Ian Murray 44 | */ 45 | public function generate_token() 46 | { 47 | if ( ! $this->CI->config->item('linkigniter.enable_csrf_protection')) 48 | { 49 | return; 50 | } 51 | 52 | // Load session library if not loaded 53 | $this->CI->load->library('session'); 54 | 55 | if ($this->CI->session->userdata(self::$token_name) === FALSE) 56 | { 57 | // Generate a token and store it on session, since old one appears to have expired. 58 | self::$token = bin2hex(openssl_random_pseudo_bytes(16)); 59 | 60 | $this->CI->session->set_userdata(self::$token_name, self::$token); 61 | } 62 | else 63 | { 64 | // Set it to local variable for easy access 65 | self::$token = $this->CI->session->userdata(self::$token_name); 66 | } 67 | } 68 | 69 | // ----------------------------------------------------------------------------------- 70 | 71 | /** 72 | * This injects hidden tags on all POST forms with the csrf token. 73 | * Also injects meta headers in of output (if exists) for easy access 74 | * from JS frameworks. 75 | * 76 | * @return void 77 | * @author Ian Murray 78 | */ 79 | public function inject_tokens() 80 | { 81 | if ( ! $this->CI->config->item('linkigniter.enable_csrf_protection')) 82 | { 83 | // This has to be here otherwise nothing is sent to the browser 84 | $this->CI->output->_display($this->CI->output->get_output()); 85 | return; 86 | } 87 | 88 | $output = $this->CI->output->get_output(); 89 | 90 | // Inject into form 91 | $output = preg_replace('/(<(form|FORM)[^>]*(method|METHOD)="(post|POST)"[^>]*>)/', 92 | '$0', 93 | $output); 94 | 95 | // Inject into 96 | $output = preg_replace('/(<\/head>)/', 97 | '' . "\n" . '' . "\n" . '$0', 98 | $output); 99 | 100 | $this->CI->output->_display($output); 101 | } 102 | 103 | // ----------------------------------------------------------------------------------- 104 | 105 | /** 106 | * Validates a submitted token when POST request is made. 107 | * 108 | * @return void 109 | * @author Ian Murray 110 | */ 111 | public function validate_tokens() 112 | { 113 | if ( ! $this->CI->config->item('linkigniter.enable_csrf_protection')) 114 | { 115 | return; 116 | } 117 | 118 | // Is this a post request? 119 | // @link http://stackoverflow.com/questions/1372147/php-check-whether-a-request-is-get-or-post 120 | if ($_SERVER['REQUEST_METHOD'] == 'POST') 121 | { 122 | // Is the token field set and valid? 123 | $posted_token = $this->CI->input->post(self::$token_name); 124 | if ($posted_token === FALSE || $posted_token != $this->CI->session->userdata(self::$token_name)) 125 | { 126 | // Invalid request, send error 400. 127 | show_error('Request was invalid. Tokens did not match.', 400); 128 | } 129 | } 130 | } 131 | } 132 | --------------------------------------------------------------------------------