Codeigniter Security
Codeigniter Security
Codeigniter Security
First escape variables from login form and then enter into database
Example
<?php
$con=mysqli_connect("localhost","my_user","my_password","my_db");
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
if (!mysqli_query($con,$sql)) {
die('Error: ' . mysqli_error($con));
}
echo "1 record added";
mysqli_close($con);
?>
Storing Passwords
There are two separate yet related methods for storing password information. One way is to
encrypt them. Another is to hash them. Both encrypting and hashing information turn a normal
string into gobbledygook. The principle difference between them, other than the technogeekish
details, is that the encrypted information can be decrypted, whereas hashed information cannot
be unhashed. On the Web, hashing is usually used.
There are a variety of hashing algorithms out there. PHP supports several dozen of them. The
most popular hashing algorithms are MD5 and SHA. MD5 is not as strong as SHA and I don’t
recommend it. I recommend versions from the SHA2 family that have two functions: SHA-256
and SHA-512.
A one-way hash makes it a little more difficult to hack a password but not impossible. It might
not be easy, but it could be done with sufficient resources.
A good way to increase your security is to add a salt to your passwords. A salt is a string of
characters that are added to a string prior to hashing it. You can add the salt to the string at the
beginning, end, or some point in the middle of the string.
A salt is a string of characters that are added to a string prior to hashing it.
There are many theories about how to effectively salt passwords. The most secure way is to use a
different salt for each password. When you save a password, generate a salt using any one of a number
of methods. A simple method is to salt with a newly generated UUID (you could theoretically salt with
the ID on the user record and kill two birds with one stone). You add the salt to the user password, hash
it, and then store it. If you are using a different salt with every user, you need to store the salt in the
database too.
<?php
$number = 9;
$str = "Beijing";
$txt = sprintf("There are %u million bicycles in %s.",$number,$str);
echo $txt;
?>
UUID Generator:
function gen_uuid() {
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
According to MySQL, AES encryption (Advanced Encryption Standard) is the best method
available for providing reversible encryption and decryption in SQL.
Formerly known as Rijndael, the AES_ENCRYPT and AES_DECRYPT functions are now built-in to
MySQL so you can take user data, encrypt it with a salt, store it in your database, then extract it
again later and decrypt it.
You'll need to apply a salt to the data that you encrypt. This is a special code that the encryption
algorithm uses which works a bit like a key.
You'll need to provide the exact same key back to decrypt the data, and if an attacker should gain
access to your database, they won't be able to decipher it without knowing the salt.
If you define your salt in PHP like this, you'll be able to pull the constant into your SQL
statements more easily.
if(!define('SALT')) define('SALT','897sdn9j98u98jk');
To insert data into your MySQL database and encrypt the sensitive information, you'll need to
issue a command like this, along with your salt.
This will insert the username in plain text, as it's non-sensitive, but encrypt the user's email and
shoesize, to prevent them from being viewed without access to the salt.
At some point, you're going to need to access some of the data you stored in its encrypted form,
and you can do this very easily using the AES_DECRYPT function of MySQL and the same salt
you used when you encrypted the data and inserted it.
If you SELECT the encrypted data without running it through AES_DECRYPT or with the
wrong or no salt, you'll get an ugly, unreadable string of odd characters. This means if an
attacker manages to access your database, but does not have access to your server to view the
salt, they won't be able to read any of the data you've stored. At least, not without going to great
lengths to try and decrypt the data.
Updating encrypted records is very similar to insertion. Basically, you just apply the same salt
and re-issue the AES_ENCRYPT command to re-encrypt the data again and lock it away safely.
Things get a little bit more complicated when you need to search for data that's encrypted and
then display it in its unencrypted form.
Say you wanted to search for a user using their email address, but you'd encrypted that in the
database. First, you'd need to encrypt the email address you want to search for with
AES_ENCRYPT and your salt, and then you'd need to use AES_DECRYPT to ensure that
MySQL decrypted it, returning it in a readable format.
Comparing Passwords
Comparing passwords is slightly trickier when storing them in hashed format. Because you
cannot unhash the passwords, the only way to compare a password entered at login to the stored
password in the database is to salt and hash the entered password and then compare it to the
stored value.
$cemail = $_POST["cemail"];
$salt = (fetch salt value)
$cpassword = hash("SHA256",
$salt.$_POST['cpassword']);
Also
A one-way hash makes it a little more difficult to hack a password but not impossible
Hashing uri segments:
Method 1:
As you will see later on in the code, there are times when it is necessary to pass information on
the URL that you don’t want a user to see. For example, if you are sending through the ID of a
row, you can keep that information to yourself. In those cases, encrypt the URI segment that has
that information.
function _uriencode($tcURI){
$encoded = base64_encode($this->encrypt->
encode($tcURI));
return $encoded;
}
function _uridecode($tcEncoded){
$decoded = $this->encrypt->
decode(base64_decode($tcEncoded));
return $decoded;
}
Use:
$ci->_uriencode($row['id']); replace your $row['id'] with your variable
You may recall entering an encryption key in config.php. It was intended, at the very least, for
sessions. If you want to use encryption in CodeIgniter as I do here, you have to go a little further
in your config file.
'a-z 0-9~%.:_\-';
The slash in the string of allowed characters is a problem. If you have a / in a URI, it is
interpreted as the end of the segment. You need to remove that slash from the list of allowed
characters so that your encrypted URI segments work properly. Here’s what the modified config
line looks like:
$config['permitted_uri_chars'] =
'+=\a-z 0-9~%.:_-';
Method 2:
First of all , you should encrypt your string as usual , then you add one step to eliminate bad
characters inside your encrypted string which are :
You should replace these characters with some other special characters that will not effect the
browser logic in loading pages .
Here is an example :
1</pre>
2this->load->library('encrypt');
$enc_username=$this->encrypt->encode($username);
3$enc_username=str_replace(array('+', '/', '='), array('-', '_', '~'),
4$enc_username);
5<pre>
1</pre>
2$this->load->library('encrypt');
$dec_username=str_replace(array('-', '_', '~'), array('+', '/', '='),
3$enc_username);
4$dec_username=$this->encrypt->decode($dec_username);
5<pre>
Autoloading a Model
There’s one last piece to cover before I can go into setupadmin(). If you look at the code to
setupadmin that is part of Listing 1, you will notice that the first bit of code immediately calls
users_model (Listing 3) without loading it.
You can autoload models by modifying the autoload.php configuration file but I don’t do that for
models that are specific to a particular set of controllers. Users_model is only relevant within the
users controller so I only want to load it when I am using controllers within users.php.
Fortunately, there is another way to autoload the model when running within users.php: put the
load code in the constructor. Since the constructor always runs when the class is instantiated,
putting a standard $this->load->model call in the constructor covers any method in the class.
Advertisement
function __construct(){
parent::__construct();
$this->load->model("users_model");
}
|--------------------------------------------------------------------------
| Determines whether the XSS filter is always active when GET, POST or
*/
$config['global_xss_filtering'] = TRUE;
The Input class has the ability to filter input automatically to prevent cross-site scripting attacks.
If you want the filter to run automatically every time it encounters POST or COOKIE data you
can enable it by opening your application/config/config.php file and setting this:
$config['global_xss_filtering'] = TRUE;
CodeIgniter comes with a Cross Site Scripting prevention filter, which looks for commonly used
techniques to trigger JavaScript or other types of code that attempt to hijack cookies or do other
malicious things. If anything disallowed is encountered it is rendered safe by converting the data
to character entities.
To filter data through the XSS filter use the xss_clean() method:
function __construct(){
parent::__construct();
$this->load->helper('security');
}
$data = $this->security->xss_clean($data);
An optional second parameter, is_image, allows this function to be used to test images for
potential XSS attacks, useful for file upload security. When this second parameter is set to
TRUE, instead of returning an altered string, the function returns TRUE if the image is safe, and
FALSE if it contained potentially malicious information that a browser may attempt to execute.
Important
Security class:
Class Reference
class CI_Security
Tries to remove XSS exploits from the input data and returns the cleaned string. If the
optional second parameter is set to true, it will return boolean TRUE if the image is safe
to use and FALSE if malicious data was detected in it.
Important
This method is not suitable for filtering HTML attribute values! Use html_escape() for
that instead.
Tries to sanitize filenames in order to prevent directory traversal attempts and other
security threats, which is particularly useful for files that were supplied via user input.
$filename = $this->security->sanitize_filename($this->input-
>post('filename'));
$filename = $this->security->sanitize_filename($this->input-
>post('filename'), TRUE);
Especially it dependes on where the input goes. If you're concerned about XSS attacks, you
could pass a TRUE as second paramether and have the XSS filter be applied to that input (but
beware as the operation is quite costrly in term of resources, so don't use wildly)
$this->input->post('name', TRUE)
If the input is going into the database, than you either escape it manually with $this->db-
>escape() (and its other cousins), or you use query bindings or, for the sake of speed and
simplicity, you can rely to the Active Record Class which automatically escapes all dats entering
the query.
(this if you don't want to use your custom escaping, with mysql_real_escape_string, or the
mysqli_ and PDO prepared statements)
Input Class:
The second optional parameter lets you run the data through the XSS filter. It’s enabled by
setting the second parameter to boolean TRUE or by setting your $config['global_xss_filtering']
to TRUE.
$this->input->post('some_data', TRUE);
To return all POST items and pass them through the XSS filter set the first parameter NULL
while setting the second parameter to boolean TRUE.
To return an array of multiple POST parameters, pass all the required keys as an array.
$this->input->post(array('field1', 'field2'));
Same rule applied here, to retrieve the parameters with XSS filtering enabled, set the second
parameter to boolean TRUE.
Security class:
Class Reference
class CI_Security
Tries to remove XSS exploits from the input data and returns the cleaned string. If the
optional second parameter is set to true, it will return boolean TRUE if the image is safe
to use and FALSE if malicious data was detected in it.
Important
This method is not suitable for filtering HTML attribute values! Use html_escape() for
that instead.
Tries to sanitize filenames in order to prevent directory traversal attempts and other
security threats, which is particularly useful for files that were supplied via user input.
$filename = $this->security->sanitize_filename($this->input-
>post('filename'));
$filename = $this->security->sanitize_filename($this->input-
>post('filename'), TRUE);
$config['csrf_protection'] = TRUE;
If you use the form helper, then form_open() will automatically insert a hidden csrf field in your
forms.
You can enable CSRF protection by altering your application/config/config.php file in the
following way:
$config['csrf_protection'] = TRUE;
If you use the form helper, then form_open() will automatically insert a hidden csrf field in your
forms. If not, then you can use get_csrf_token_name() and get_csrf_hash()
$csrf = array(
'name' => $this->security->get_csrf_token_name(),
'hash' => $this->security->get_csrf_hash()
);
...
Tokens may be either regenerated on every submission (default) or kept the same throughout the
life of the CSRF cookie. The default regeneration of tokens provides stricter security, but may
result in usability concerns as other tokens become invalid (back/forward navigation, multiple
tabs/windows, asynchronous actions, etc). You may alter this behavior by editing the following
config parameter
$config['csrf_regenerate'] = TRUE;
Tokens may be either regenerated on every submission (default) or kept the same throughout the
life of the CSRF cookie. The default regeneration of tokens provides stricter security, but may
result in usability concerns as other tokens become invalid (back/forward navigation, multiple
tabs/windows, asynchronous actions, etc). You may alter this behavior by editing the following
config parameter
$config['csrf_regenerate'] = TRUE;
Select URIs can be whitelisted from csrf protection (for example API endpoints expecting
externally POSTed content). You can add these URIs by editing the ‘csrf_exclude_uris’ config
parameter:
$config['csrf_exclude_uris'] = array('api/person/add');
$config['csrf_exclude_uris'] = array(
'api/record/[0-9]+',
'api/title/[a-z]+'
);
Default Config file settings:
/*
|---------------------------------------------------------------
-----------
| Cross Site Request Forgery
|---------------------------------------------------------------
-----------
| Enables a CSRF cookie token to be set. When set to TRUE, token
will be
| checked on a submitted form. If you are accepting user data,
it is strongly
| recommended CSRF protection be enabled.
|
| 'csrf_token_name' = The token name
| 'csrf_cookie_name' = The cookie name
| 'csrf_expire' = The number in seconds the token should expire.
| 'csrf_regenerate' = Regenerate token on every submission
| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
*/
$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrf_tk_name';
$config['csrf_cookie_name'] = 'csrf_ck_name';
$config['csrf_expire'] = 60;
$config['csrf_regenerate'] = TRUE;
$config['csrf_exclude_uris'] = array();
/*
1- $this->load->library('encryption');
By default, the Encryption Library will use the AES-128 cipher in CBC mode, using your
configured encryption_key and SHA512 HMAC authentication.
Note
AES-128 is chosen both because it is proven to be strong and because of its wide
availability across different cryptographic software and programming languages’ APIs.
In order to create a proper key, you must use the Encryption library’s create_key() method
The key can be either stored in your application/config/config.php, or you can design your own
storage mechanism and pass the key dynamically when encrypting/decrypting.
To save your key to your application/config/config.php, open the file and set:
You’ll notice that the create_key() method outputs binary data, which is hard to deal with (i.e. a
copy-paste may damage it), so you may use bin2hex(), hex2bin() or Base64-encoding to work
with the key in a more friendly manner. For example:
3- If you wish to change that driver settings and more, you need to use the initialize()
method. It accepts an associative array of parameters, all of which are optional:
For example, if you were to change the encryption algorithm and mode to AES-256 in CTR
mode, this is what you should do:
$this->encryption->initialize(
array(
'cipher' => 'aes-128',
'mode' => 'cbc',
'key' => '<a 32-character random string>'
)
);
Note that we only mentioned that you want to change the ciper and mode, but we also included a
key in the example. As previously noted, it is important that you choose a key with a proper size
for the used algorithm.
There’s also the ability to change the driver, if for some reason you have both, but want to use
MCrypt instead of OpenSSL:
function __construct(){
parent::__construct();
$this->load->library('encryption'); //for database storage encryption
$key = bin2hex($this->encryption->create_key(16));
$this->encryption->initialize(
array(
'cipher' => 'aes-128',
'mode' => 'cbc',
'key' => ' hex2bin(<your hex-encoded key>)'
)
);
$this->encryption->initialize(array('driver' => 'openssl'));
}
Encrypton and Decryption:
$plain_text = 'This is a plain-text message!';
$ciphertext = $this->encryption->encrypt($plain_text);
Image Security:
XSS Filtering
CodeIgniter comes with a Cross Site Scripting prevention filter, which looks for commonly used
techniques to trigger JavaScript or other types of code that attempt to hijack cookies or do other
malicious things. If anything disallowed is encountered it is rendered safe by converting the data
to character entities.
To filter data through the XSS filter use the xss_clean() method:
$data = $this->security->xss_clean($data);
An optional second parameter, is_image, allows this function to be used to test images for
potential XSS attacks, useful for file upload security. When this second parameter is set to
TRUE, instead of returning an altered string, the function returns TRUE if the image is safe, and
FALSE if it contained potentially malicious information that a browser may attempt to execute.
File Security:
Tries to sanitize filenames in order to prevent directory traversal attempts and other
security threats, which is particularly useful for files that were supplied via user input.
$filename = $this->security->sanitize_filename($this->input-
>post('filename'));
$filename = $this->security->sanitize_filename($this->input-
>post('filename'), TRUE);
Session security:
sessions are stored in session cookie.
If in config file 'sess_encrypt_cookie' option is given(in some
versions it is not given)
If the user changes the content of the session cookie in the browser, CodeIgniter will notice on
the next server call, and create a new session for the user, basically logging him out.
CodeIgniter doesn't really need the data stored in the cookie in the user's browser, and as long as
you're using
$this->session->userdata('userid');
you're going to get trusted server-side data. The user can't change that. Furthermore, the cookie
can be encrypted, and you should have it encrypted. Just look in config.php of CodeIgniter.
There are several other protections around the session data: the short refresh timeout (usually 300
seconds), it checks if the IP changed, and if the browser changed. In other words, in the worst
case scenario, the only way to spoof the session data is by having the same version of the
browser, having the same IP, getting direct access to the computer to copy/paste the cookie, and
getting this done within 5 minutes.
For encrypt the cookie in codeigniter make changes in the config.php file at
$config['sess_encrypt_cookie'] = FALSE;
$config['sess_encrypt_cookie'] = TRUE;
$config['encryption_key'] = "";
/*
|---------------------------------------------------------------
-----------
| Cookie Related Variables
|---------------------------------------------------------------
-----------
|
| 'cookie_prefix' = Set a cookie name prefix if you need to
avoid collisions
| 'cookie_domain' = Set to .your-domain.com for site-wide
cookies
| 'cookie_path' = Typically will be a forward slash
| 'cookie_secure' = Cookie will only be set if a secure HTTPS
connection exists.
| 'cookie_httponly' = Cookie will only be accessible via HTTP(S)
(no javascript)
|
| Note: These settings (with the exception of 'cookie_prefix'
and
| 'cookie_httponly') will also affect sessions.
|
*/
$config['cookie_prefix'] = 'ck';
$config['cookie_domain'] = 'give your domain name here';
$config['cookie_path'] = '/';
$config['cookie_secure'] = FALSE;
$config['cookie_httponly'] = TRUE;
/*
|--------------------------------------------------------------------------
| Determines whether the XSS filter is always active when GET, POST or
*/
$config['global_xss_filtering'] = TRUE;
$this->input->cookie('some_cookie');
$this->input->cookie('some_cookie', TRUE); // with XSS filter
To return an array of multiple cookie values, pass all the required keys as an array.
$this->input->cookie(array('some_cookie', 'some_cookie2'));
Note
Unlike the Cookie Helper function get_cookie(), this method does NOT prepend your
configured $config['cookie_prefix'] value.
|--------------------------------------------------------------------------
| Determines whether the XSS filter is always active when GET, POST or
*/
$config['global_xss_filtering'] = TRUE;
set_cookie($name = ''[, $value = ''[, $expire = ''[, $domain = ''[, $path = '/'[, $prefix = ''[,
$secure = NULL[, $httponly = NULL]]]]]]])
$name (mixed) – Cookie name or an array of parameters
$value (string) – Cookie value
$expire (int) – Cookie expiration time in seconds
Parameters:
$domain (string) – Cookie domain
$path (string) – Cookie path
$prefix (string) – Cookie name prefix
$secure (bool) – Whether to only transfer the cookie through
HTTPS
$httponly (bool) – Whether to only make the cookie accessible for
HTTP requests (no JavaScript)
Return
void
type:
Sets a cookie containing the values you specify. There are two ways to pass information
to this method so that a cookie can be set: Array Method, and Discrete Parameters:
Array Method
$cookie = array(
'name' => 'The Cookie Name',
'value' => 'The Value',
'expire' => '3600',
'domain' => '.some-domain.com',
'path' => '/',
'prefix' => 'cki_',
'secure' => TRUE,
'httponly' => TRUE
);
$this->input->set_cookie($cookie);
Notes
Only the name and value are required. To delete a cookie set it with the expiration blank.
The expiration is set in seconds, which will be added to the current time. Do not include
the time, but rather only the number of seconds from now that you wish the cookie to be
valid. If the expiration is set to zero the cookie will only last as long as the browser is
open.
For site-wide cookies regardless of how your site is requested, add your URL to the
domain starting with a period, like this: .your-domain.com
The path is usually not needed since the method sets a root path.
The prefix is only needed if you need to avoid name collisions with other identically
named cookies for your server.
The httponly and secure flags, when omitted, will default to your
$config['cookie_httponly'] and $config['cookie_secure'] settings.
Discrete Parameters
If you prefer, you can set the cookie by passing data using individual parameters:
Others:
ip_address()
Returns: Visitor’s IP address or ‘0.0.0.0’ if not valid
Return type: string
Returns the IP address for the current user. If the IP address is not valid, the method will
return ‘0.0.0.0’:
echo $this->input->ip_address();
Important
This method takes into account the $config['proxy_ips'] setting and will return the
reported HTTP_X_FORWARDED_FOR, HTTP_CLIENT_IP, HTTP_X_CLIENT_IP or
HTTP_X_CLUSTER_CLIENT_IP address for the allowed IP addresses.
Note
if ( ! $this->input->valid_ip($ip))
{
echo 'Not Valid';
}
else
{
echo 'Valid';
}
user_agent([$xss_clean = NULL])
Returns: User agent string or NULL if not set
$xss_clean (bool) – Whether to apply XSS filtering
Parameters:
Return type: mixed
Returns the user agent string (web browser) being used by the current user, or NULL if
it’s not available.
echo $this->input->user_agent();
See the User Agent Class for methods which extract information from the user agent
string.