Welcome Guest, Not a member yet? Register   Sign In
CI and a user login system, I want to verify if what I think is correct.
#1

[eluser]lanzd[/eluser]
I am new to CI and have some user login code that I currently use and was trying to adapt it for CI. Basically I check the information the user entered against my database and if it is valid I set two cookie variables: user_id and loggin_in. Then on every page which requires restrictions I call a function which takes the user_id cookie and compares it to my database to see if it is a valid user.

For my adaptation I was thinking of just throwing that function into /application/helpers/login_helper.php. I then realized my helpers are not supposed to directly access my models. I found a tutorial on this site: http://www.codefactorycr.com/login-with-...r-php.html which simply wraps the contents of each function which requires restriction in
Code:
if($this->session->userdata('logged_in'))
13    { }
to check the session cookie.

Is this secure because of the settings in CI's config.php:

Code:
$config['encryption_key'] = 'something here';
$config['sess_encrypt_cookie'] = TRUE;
$config['sess_use_database'] = True;

This link here is what I'm referring to: http://ellislab.com/codeigniter/user-gui...sions.html

Quote:When session data is available in a database, every time a valid session is found in the user's cookie, a database query is performed to match it. If the session ID does not match, the session is destroyed. Session IDs can never be updated, they can only be generated when a new session is created.

This automatically encrypts and decrypts the session cookies every time a valid session is found in the user's cookie? Or does this mean it stores each cooking in my database table and checks it for authentication? If the latter is the case, then I'm assuming it stores an array of custom session cookies in the field
Quote:user_data
in the default tables suggested:

Code:
CREATE TABLE IF NOT EXISTS  `ci_sessions` (
session_id varchar(40) DEFAULT '0' NOT NULL,
ip_address varchar(45) DEFAULT '0' NOT NULL,
user_agent varchar(120) NOT NULL,
last_activity int(10) unsigned DEFAULT 0 NOT NULL,
user_data text NOT NULL,
PRIMARY KEY (session_id),
KEY `last_activity_idx` (`last_activity`)
);

Many thanks, Dan



#2

[eluser]PhilTem[/eluser]
I personally think it's never secure to store a user-ID in a cookie as long as your doing with a cookie called 'user_id' and/or store the user-ID as blank text i.e. not encrypted. In that case it's very easy for anyone to alter the cookie content and login as the administrator - especially if you're using integers as user-IDs since the admin in most cases has ID == 1.

Nevertheless, I'd say, your approach is quite good, however you might want to - that's what I did to add another level of security - add another table called e.g. "user_sessions" which stores the user_id and a hash (can be a random string or a sha1 of the user-id and some salt). This hash is stored in the user's session (must not be a cookie) and on every page request I just compare the value of the hash to be a valid row in the "user_sessions" table. If that is given I assume the user is logged in. In this case it will be very hard to hijack a session since nobody really knows how I created the hash and/or there are too many possibilities for just finding one user-ID.

Furthermore you might want to create a authentication library that will handle all the things such as login, logout, register, forgotten_password, is_logged_in, ... Just to have a common place to refer to. You may also pack these functions in helpers but then I'd recommend using a library/model on the backend so that the helper-functions are simple wrappers for the library/model-functions.
#3

[eluser]lanzd[/eluser]
- Disregard this paragraph, I meant to remove from my reply before I posted, but forgot to | Am I correct when thinking that I should not access my models from within my helpers? Logically I can only guess in every controller I have to run a query to select from the. “user_sessions” table where it's hash field matches the users cookie (which is unhashed?) | - Disregard this paragraph, I meant to remove from my reply before I posted, but forgot to.

So have a tables with the the fields 'user_id' and 'hash' where 'user_id' is the users id in my login table and hash is some random string. Then using the users id and that hash field to actually hash the users id and store that value in the user's session. Then upon each page request hash the two fields and compare it to the user's session?

I think each page request is when each function in my controllers is called. Does this mean I have to wrap the contents of each function in my controllers in an if which checks all of this out? Can I put this in the constructor or somewhere else which would require less reworking? Or am I incorrect in this thought?

Quick question: is this http://ellislab.com/codeigniter/user-gui...sions.html the equivalent of $_SESSION[''] in php? And this is what you are referring to in the line
Quote:This hash is stored in the user’s session (must not be a cookie)

And is this http://ellislab.com/codeigniter/user-gui...elper.html the equivalent of $_COOKIE[''] in php?


Thanks, Dan

Edit: Am I correct when thinking that I should not access my models from within my helpers? If so, that means I have to run the query in my controller and then pass the necessary data to the helper and let it do the validation from there. Correct?
#4

[eluser]PhilTem[/eluser]
Let me first answer the short questions:

Nope, $_SESSION is not the same as the sessions library. There's a native PHP session implementation as an additional library, but for using $this->session you are not accessing $_SESSION but data that is store in the database. Only the session_id is stored on the user site (I'm not sure if it's store in $_SESSION or $_COOKIE)

Yes, $_COOKIE is $this->input->cookie() as far as I remember it correctly Wink

I personally think that you can call a model's function within a helper as libraries are just kinda another thing as helpers. In pure MVC libraries don't exist and you just have models, views, controllers, and helpers. Libraries are also kinda models of models i.e. combining multiple models into on "supervisor" model. Imagine again an auth-library. You'd have - for best MVC - a model for each table that you have. Assuming you have the following sets of tables ['users', 'user_sessions', 'user_cookies', 'user_login_attempts'] then you'll end up with 4 models. All of them just have limited functionality (crud operations on the tables) but combined in the auth library you can create a login-method to combine crud operations on every single table by just calling one method. So to speak: If a user logs in you want to check that he/she has not exceeded the maximum false login attempts (calling the user_login_attempts_m), then check the credentials (calling the user_model), then see if he/she wants to be remembered (calling the user_cookies_m). And so on and so forth.
That being said and summarizing it: Libraries are kinda like supervising models or combining multiple method calls to the models into one method (for keeping it DRY Wink )


Maybe I should explain my structure with a little more code examples:

Table user_sessions has fields ['session_id', 'rel_user_id', 'hash'] where 'rel_user_id' is a foreign key to your users-table. 'session_id' is just an auto-increment value so that we have a primary key in the table. 'hash' is composed of

Code:
$salt = random_string('alnum', 32);
$hash = sha1($user_id . $salt);

but may also be any of these

Code:
$hash = sha1($user_id);
$hash = md5($user_id);
$hash = $this->encrypt->encode($user_id);

or other things e.g. appending time() to $user_id before hashing it. It should just be something that cannot be decoded easily - it'd be best if it cannot be decoded at all Wink

Now, what you'd do after having created the hash is inserting a row in the database and if that was successful you can write the hash to the user session using

Code:
$this->session->set_userdata('login_id', $hash);

I like calling the session-key 'login_id' because it is an identifier for the login.

Your function logged_in() could be like this (I'm assuming it is in a library or model)
Code:
function logged_in() {
  $hash = $this->session->userdata('login_id');
  
  if ( ! $hash )  return FALSE;
  
  $query = $this->db->select('session_id')->from('user_sessions')->where('hash', $hash);
  
  return $query->num_rows() == 1;
}

That would really be the shortest for of checking if a user is logged in using a database on your side. If you will be calling logged_in() multiple times on a request you don't want to have more than one database query so you could use some static variables like

Code:
function logged_in() {
  static $validated;
  
  if ( ! is_null($validated) ) {
    return $validated;
  }
  
  $hash = $this->session->userdata('login_id');
  
  if ( ! $hash )  return ( $validated = FALSE  );
  
  $query = $this->db->select('session_id')->from('user_sessions')->where('hash', $hash);
  
  return $validated = ( $query->num_rows() == 1 );
}

I hope having answered all your questions, if not, just keep asking Wink
#5

[eluser]lanzd[/eluser]
Okay, that helps a lot. I just want to reiterate to make sure I have it.

The line
Code:
$this->session->set_userdata('login_id', $hash);
is storing the value of hash in a database table. Not the classic
Code:
$_SESSION[]

Is this the table it is storing it in?
Code:
CREATE TABLE IF NOT EXISTS  `ci_sessions` (
session_id varchar(40) DEFAULT '0' NOT NULL,
ip_address varchar(45) DEFAULT '0' NOT NULL,
user_agent varchar(120) NOT NULL,
last_activity int(10) unsigned DEFAULT 0 NOT NULL,
user_data text NOT NULL,
PRIMARY KEY (session_id),
KEY `last_activity_idx` (`last_activity`)
);

If so, it is just storing all of my variables set with
Code:
$this->session->set_userdata('Name', 'Value');
as an array in the "user_data" field, correct?

Along the lines of Libraries and Models, It is considered best practice to have a model for each database table and group actions which require multiple actions from a model(s) in a library, is that correct?

Thank you for all of the help, Dan
#6

[eluser]PhilTem[/eluser]
You're right, using CI's session library stores the user data (the array will be serialized) in the field 'user_data' inside the table 'ci_sessions' (or however else you called the table Wink ).

I personally would countersign your last statement about libraries, models, and database tables yet I know there are several people out there that do not share my opinion. Plus, if you have your database normalized by the third normal form you'd end up with hundreds of models because you have so many relational tables. But for me, that's okay because I basically use some of my models not for retrieving data but just for storing the table name and its fields in one common place that is a model file. Some of my models don't serve another purpose but
Code:
$this->model_name->table();
// OR
$this->model_name->fields();

to provide the table name and fields joins I perform in my libraries Wink




Theme © iAndrew 2016 - Forum software by © MyBB