|
gwar3k1 posted:So I should be encapsulating my "action" scripts with the session authorization and not just setting a variable that says the user is logged in and the session is valid? That makes sense. No, he's saying make sure the page your form submits to authenticates the session as well. That's assuming the script isn't submitting to itself.
|
# ? Dec 21, 2009 22:47 |
|
|
# ? Jun 3, 2024 19:35 |
|
gwar3k1 posted:So I should be encapsulating my "action" scripts with the session authorization and not just setting a variable that says the user is logged in and the session is valid? That makes sense. These are functionally identical, as long as 'gently caress off' stops script execution: php:<? $auth = is_valid_session(); if($auth === FALSE) { header('Location: [url]http://example.com/fuckoff.html[/url]'); exit; } // and now on with the show... ?> If someone gets to a form then gets told to gently caress off, its not the greatest user experience, but its not insecure. If they are told they can't have the form, but could send a carefully crafted POST or GET and take the action anyway...you have failed at a very fundamental level.
|
# ? Dec 21, 2009 23:03 |
|
Ah okay, so I can have my session validation separate so long as everything that runs behind a login is validated.php:<?php session_start(); include('session.php'); $valid = validate_session(); if($valid) { //code } else { header("location:login.php"); } ?>
|
# ? Dec 21, 2009 23:03 |
|
What's the best way to sanitize input that's going to be stored in a DB? For example, I want to make sure that someone can't add a post that contains JavaScript. I'd like to allow links and images, but I don't want to bother making a markup language like BBCode or anything like that. I could add a bunch of RegExes to find these, but invariably someone will break them, and there has to be dozens of things I'm not thinking about. Blacklisting like this might not be the way to go. Is there an easy way to sanitize the data, or to create a whitelist of "approved" HTML tags? I'm thinking I could use a RegEx to catch all the tags, and compare them to an array of allowed tags. There has to be something cleaner / faster than this.
|
# ? Dec 21, 2009 23:18 |
|
Inverse Icarus posted:What's the best way to sanitize input that's going to be stored in a DB? Also let me know how it works out - I researched these for a previous project but never ended up using it. supster fucked around with this message at 23:30 on Dec 21, 2009 |
# ? Dec 21, 2009 23:27 |
|
Inverse Icarus posted:What's the best way to sanitize input that's going to be stored in a DB? Probably not comprehensive in any way but PHP has strip_tags which you can add a white list of tags. I don't think it plays well with tags with attributes though.
|
# ? Dec 21, 2009 23:29 |
|
php:<? function hl_ent($t){ // entitity handler global $C; ?> meaningful names are for noobs also, look at the entitities on that one (I'm sure it works fine, but drat) Edit: they have a variable caled $ok2...its an array!
|
# ? Dec 21, 2009 23:54 |
|
KuruMonkey posted:Edit: they have a variable caled $ok2...its an array! To be fair, the keystrokes you save are well worth the slight loss in clarity of having to type out $okAsWell every time.
|
# ? Dec 22, 2009 01:33 |
|
Lumpy posted:To be fair, the keystrokes you save are well worth the slight loss in clarity of having to type out $okAsWell every time.
|
# ? Dec 22, 2009 02:22 |
|
Yeah I mean basically be boiled down to: code for the controller that handles the submitted form: code:
|
# ? Dec 22, 2009 02:32 |
|
supster posted:Or it could just be named something that is meaningful in context of what the variable represents... You don't "get" jokes, do you.
|
# ? Dec 22, 2009 02:48 |
|
Lumpy posted:You don't "get" jokes, do you.
|
# ? Dec 22, 2009 02:56 |
|
Alsoquote:auth = false This poo poo should be done by your session class. Your session class should contain all logic required to be trust-able. This means if your method you had pasted in all your scripts turns out to be horribly insecure, you are sorted. Talking of which, could someone quickly read through and give mine a sanity check? It is based on the assumption that a quick database query on an active connection is pretty much as quick as a file access (it replaces PHP's $_SESSION): http://github.com/radiosilence/core/blob/master/session.php php:<?php /** * Session management. * @package auth * @subpackage core */ class session { /** * Database * @var database */ private $db; /** * Session sid. * @var string */ private $session = 0; /** * Session token. * @var string */ private $tok = 0; /** * Session user id. * @var int */ public $user_id = 0; /** * Session data. * @var string */ private $data = array(); /** * Secret phrase. * @var string */ private $keyphrase; /** * Secret phrase to salt passwords. * @var string */ private $base_salt; /** * Starts it all off, gets the sid/tok provided by * the cookie, and authorises it/registers it as * valid depending on the result. * @param database $db database object. */ public function __construct( $db ) { require( SITE_PATH . "configuration/auth.php" ); $this->keyphrase = $config_auth[ 'keyphrase' ]; $this->base_salt = $config_auth[ 'base_salt' ]; $this->db = $db; $sid = $_COOKIE[ "sid" ]; $tok = $_COOKIE[ "tok" ]; if( isset( $_COOKIE[ "sid" ] ) && isset( $_COOKIE[ "tok" ] ) ) { if(DEBUG) FB::log( "Attempting to load supposed session [" . $sid . "] ..." ); # Is it the right tok for sid and IP? $sth = $db->prepare( " SELECT sid, data, user_id FROM sessions WHERE sid = :sid AND tok = :tok AND ipv4 = :ipv4 LIMIT 1 " ); $e = $sth->execute( array( ":sid" => $sid, ":tok" => $tok, ":ipv4" => $_SERVER[ "REMOTE_ADDR" ] )); if( $e ) { $row = $sth->fetch(); $chall = $this->create_token( $sid ); # would a recreation of this from this host be the same as the real thing? if( $chall == $tok ) { if(DEBUG) FB::send( "Challenge: ". $chall . " Real: " . $tok, "Toks" ); $this->set_session( array( "sid" => $sid, "data" => json_decode( $row[ "data" ], true ), "tok" => $tok, "user_id" => $row[ "user_id" ] ) ); } } else { die( $db->error ); } } if( $this->session ) { if(DEBUG) FB::log( $this, "✔ Loaded session [" . $this->session . "]" ); } else { if(DEBUG) FB::log( "× Session not loaded because it doesn't exist." ); } } public function __destruct() { if( $this->session ) { if(DEBUG) FB::log( $this, "Saving session [" . $this->session . "] ... " ); $db = $this->db; $sth = $db->prepare( " UPDATE sessions SET data = :data WHERE sid = :sid LIMIT 1" ); $sth->execute( array( ":data" => json_encode( $this->data ), ":sid" => $this->session )); if( $e ) { if(DEBUG) FB::log( "✔ Saved session." ); } else { if(DEBUG) FB::error( "× Could not write session." ); } } else { if(DEBUG) FB::log( "× Not destroying session because it doesn't exist." ); } } /** * Gets stuff from data, overloader. * @param $prop_name Property * @param $prop_value Property data * @return boolean */ function __get( $prop_name ) { if ( isset( $this->data[ $prop_name ] ) ) { return $this->data[ $prop_name ]; } else { return false; } } /** * Sets stuff to data, overloader. * @param $prop_name Property * @param $prop_value Property data * @return boolean */ function __set( $prop_name, $prop_value ) { $this->data[ $prop_name ] = $prop_value; return true; } /** * Creates a session, puts it in the database, * returns the ID.. Assumes login has succeeded. * @param integer $user_id User ID * @param string $passhash Hashed password. * @param string $email User's email. * @return array Either a fail or an array with $sid, $id, and $tok. */ public function create_session( $user_id ) { $sid = $this->create_sid(); $tok = $this->create_token( $sid ); $db = $this->db; $sth = $db->prepare( " DELETE FROM sessions WHERE user_id = :user_id AND ipv4 = :ipv4" ); $sth->execute( array( "user_id" => $user_id, "ipv4" => $_SERVER[ "REMOTE_ADDR" ] )); $sth2 = $db->prepare( " INSERT INTO sessions ( sid, tok, ipv4, user_id ) VALUES ( :sid, :tok, :ipv4, :user_id ) " ); $res = $sth2->execute( array( ":sid" => $sid, ":tok" => $tok, ":ipv4" => $_SERVER[ "REMOTE_ADDR" ], ":user_id" => $user_id )); if( $res ) { $return = array( "sid" => $sid, "id" => $user_id, "tok" => $tok ); } else { $return = 0; } return $return; } /** * Destroys the session, deletes from DB, unsets cookies. * */ public function destroy_session() { $db = $this->db; $sth = $db->prepare( " DELETE FROM sessions WHERE sid = :sid AND ipv4 = :ipv4 AND tok = :tok " ); $sth->execute( array( ":sid" => $this->session, ":ipv4" => $_SERVER[ "REMOTE_ADDR" ] )); setcookie( "sid", "DEAD", time()-1, WWW_PATH . "/", null, false, true ); setcookie( "tok", "DEAD", time()-1, WWW_PATH . "/", null, false, true ); return 1; } /** * Makes a hash from a password string. * @param string $password unhashed password * @return string password hash */ public function password_hash( $password, $salt ) { $hash = hash( "sha256", $password . sha1( $salt . $this->base_salt ) ); return $hash; } /** * Sets the object's session to the right things. * @param hash $sid * @param integer $id */ public function set_session( $s ) { if(DEBUG) FB::send( $s, "Setting Session" ); $this->session = $s[ 'sid' ]; $this->user_id = $s[ 'user_id' ]; $this->data = $s[ 'data' ]; $this->tok = $s[ 'tok' ]; } /** * Sets the cookies, with httponly. * @param hash $tok */ public function set_cookie() { setcookie( "sid", $this->session, time()+(3600*24*65), WWW_PATH . "/", null, false, true ); setcookie( "tok", $this->tok, time()+(3600*24*65), WWW_PATH . "/", null, false, true ); if(DEBUG) FB::log("Setting up cookies."); } /** * Generates a new auth token based on session ID. * @param string $passhash Password hash. * @param string $email User's email. */ private function create_token( $sid ) { # Token generation code. $hash = sha1( $this->keyphrase . $_SERVER[ 'REMOTE_ADDR' ] . $sid ); return $hash; } /** * Generate a simple sid hash. * @return hash sid */ private function create_sid() { return sha1( microtime() . $_SERVER[ 'REMOTE_ADDR' ]); } } ?> Things to consider: - Should I use User Agent authentication? It just seems like a very small road hump in the face of anyone malicious. - If the script ends prematurely, the session isn't saved. This seems like a bad thing, but I actually think it is more secure, as it relies on PHP ending properly. - Perhaps one day I will use camelCase. Today is_not_the_day. Rat Supremacy fucked around with this message at 03:44 on Dec 22, 2009 |
# ? Dec 22, 2009 03:40 |
|
It's a bit messy with proxies, it's generally not a good idea to use the IP if you would like anyone in a big company to access such a site. You could put a LIMIT on the DELETE FROM SQL. MrMoo fucked around with this message at 03:59 on Dec 22, 2009 |
# ? Dec 22, 2009 03:46 |
|
For a good sessions management class, take a look at this: Database Sessions Management Class
|
# ? Dec 22, 2009 14:18 |
|
MrMoo posted:It's a bit messy with proxies, it's generally not a good idea to use the IP if you would like anyone in a big company to access such a site. LIMIT was intentionally removed - a handy way of getting rid of duped sessions if they exist for some reason - sids should be unique. I don't really know what other ways there are really to verify that a host is actually a host other than IP. The way it is done in theory allows more than one session per IP, so shared IPs shouldn't matter. Still need a mechanism for cleaning up orphan sessions, perhaps some sort of cleaning batch run nightly, and a field for expires or something.
|
# ? Dec 22, 2009 14:19 |
|
haywire posted:LIMIT was intentionally removed - a handy way of getting rid of duped sessions if they exist for some reason - sids should be unique. Useful when deleting all existing sessions in create_session() but not so when deleting a single session in destroy_session(), although you might want to allow multiple sessions anyway. REMOTE_ADDR is the address of the last proxy a client is using, if they have multiple proxies the session might bounce between different IP addresses. HTTP_X_FORWARDED_FOR might be set to the clients actual address, or even 127.0.0.1 for various reasons (security through obscurity is popular). With a Squid & HAVP sandwich it can end up a list of addresses, mine shows: "10.6.15.69, 127.0.0.1".
|
# ? Dec 23, 2009 05:39 |
|
MrMoo posted:Useful when deleting all existing sessions in create_session() but not so when deleting a single session in destroy_session(), although you might want to allow multiple sessions anyway. So which would you recommend using? HTTP_X_FORWARDED_FOR unless it == 127.0.0.1?
|
# ? Dec 23, 2009 14:32 |
|
I am normally in the .Net area, but a coworker/boss asked me to take a look at a side project of theirs and I'd love a bit of advice on how to take care of this issue for them, or at least tell them how they should proceed. They have a combobox with a text box above it serving as a javascript filter. The problem though, is that it is REALLY slow. The first time I loaded it up the page took 40 seconds or so to load, and typing into the filter can take 10+ seconds per key stroke. A quick look at the source showed why - it was returning 1.25meg response stream. Over 20,000 lines of javascript generating by a peice of php. Heres a snipped sample; PHP Code: code:
code:
code:
code:
Any thoughts guys?
|
# ? Dec 30, 2009 01:21 |
|
Yeah. As far as I can tell it's trying to do an AJAXy autosuggest, but without the AJAX. Without the AJAX you're stuck with 20000 lines of PHP-generated Javascript, so page load times will suck no matter what. The JS hunting through each element doing an indexOf is just icing on the cake. A more intelligent method would be to make a simple PHP script accepting a parameter (call it $fragment after sanitizing etc) to output the contents of the <select> based on a database query like "SELECT location_name, location_code FROM locations WHERE location_name LIKE '%$fragment%' LIMIT 100". And only have it kick in after three or so keystrokes. The the onkeyup event can just call a function that will replace the content of the <select> with whatever the server says: onkeyup="populate_select_with_suggestions(selectId,this.value)" or something like that.
|
# ? Dec 30, 2009 01:38 |
|
I'm doing a little bit of hobbyist scripting. I'm currently trying to make a random Pokemon generator where a user selects the number of Pokemon they want from a drop-down form and then it generates however many Pokemon they chose (taking the data from a MySQL database). However, I want to add a second part to the script where it can limit the random Pokemon to certain types (ex: 5 pokemon, all grass). I've gotten this script so far: http://pastebin.com/dd935973 When I run it I get this error: code:
|
# ? Jan 2, 2010 19:43 |
|
You are doing the fetch outside the if block. So the prepare is failing for which whatever reason and returning false, but because the fetch is outside of that if block, it is still trying to do fetch on $stmt, which is boolean false as opposed to a statement. In short: Your prepare is failing. A quick word of advice, avoid procedural style (definitely) and mysqli (ideally) altogether and use PDO as an object. It is much cleaner and nicer. You could rewrite: php:<? if ($stmt = mysqli_prepare($link, "SELECT * FROM pokemon WHERE type =?")) { mysqli_stmt_bind_param($stmt, 's', $type); mysqli_stmt_bind_result($stmt, $id, $name); mysqli_stmt_execute($stmt); mysqli_stmt_store_result($stmt); } $rows = array(); while(mysqli_stmt_fetch($stmt)) { $rows[] = array($id, $name); } print_r( $rows[rand()] ); ?> php:<? // Somewhere you will have initiated $db as $pdo = new PDO( "mysql:dbname=testdb;host=127.0.0.1;port=3600", "user", "pass" ); instead of doing mysqli_connect or whatever. $stmt = $db->prepare( " SELECT * FROM pokemon WHERE type = :type " ); $stmt->execute( array( ":type" => $type )); if( $rows = $stmt->fetchAll() ) { print_r( $rows[ rand() ] ); } else { die( "Bugger.\n" ); } ?> Rat Supremacy fucked around with this message at 20:25 on Jan 2, 2010 |
# ? Jan 2, 2010 20:19 |
|
Thanks, Haywire. I put in the new PDO object and tried to redo the db connection like so: php:<? $db = new PDO( "mysql:dbname=fluue_pokemon;host=localhost;port=3600", "fluue_*****", "***removed***" ); ?> php:<? do { $i++; $stmt = $db->prepare( " SELECT * FROM pokemon WHERE type = :type " ); $stmt->execute( array( ":type" => $type )); if( $rows = $stmt->fetchAll() ) { print_r( $rows[ rand() ] ); } else { die( "Bugger.\n" ); } } while($i<=$limit); ?>
|
# ? Jan 2, 2010 20:41 |
|
php:<? $stmt->execute( array( "type" => $type )); ?> Peanut and the Gang fucked around with this message at 19:56 on Jan 3, 2010 |
# ? Jan 2, 2010 22:28 |
|
Yeah, I wasn't sure what that colon was there for... However, I'm still getting the die error. It doesn't seem to be fetching the rows, though I'm not 100% sure that's the problem. edit: http://pastebin.com/m2f5d418d Here's what I've got now, in case anyone needs to see the updated code.
|
# ? Jan 2, 2010 22:36 |
|
Check out the error log. And run var_dump() or print_r() a lot. As in, wrap it around each statement and look for ones that output bool(false): php:<? var_dump($db = new PDO( "mysql:host=localhost;dbname=fluue_pokemon", "fluue_****", "****" )); //... var_dump($stmt = $db->prepare( " SELECT * FROM pokemon WHERE type = :type " )); var_dump($stmt->execute( array( "type" => $type ))); ?>
|
# ? Jan 2, 2010 23:01 |
|
code:
php:<? $stmt->execute( array( "type" => $type )) ?>
|
# ? Jan 2, 2010 23:31 |
|
That looks like the actually SELECT statement is wrong, as if the "pokemon" table or the "type" column didn't exist. Add this after the execute() call: var_dump($stmt->errorInfo());
|
# ? Jan 2, 2010 23:57 |
|
Gah, it turns out I was calling the wrong column. Had to be "type1" instead of "type". It now returns the bool as "true," but I'm still get a die error at the end. code:
|
# ? Jan 3, 2010 00:40 |
|
My guess is now none of the the rows match the WHERE clause, so it's giving a blank array. btw, when doing var_dump(), "array(0) { }" means an empty array. Syntax is "array(numberOfElements) {name=>value, ...}".
|
# ? Jan 3, 2010 00:56 |
|
Which is odd, because type1 exists in the table and I know for a fact that many of the entries -should- match the WHERE clause. Even setting it to Any where it makes $type = "*" gives me an empty array.
|
# ? Jan 3, 2010 01:23 |
|
That's actually looking for the string "*"; it doesn't act like a wildcard here. But you can use the LIKE keyword, which sees '%' as a wildcard. So on case 0, set $type='%'; Then edit the WHERE clause: WHERE type1 LIKE :type1
|
# ? Jan 3, 2010 01:56 |
|
I think that fixed something: the execute query grabs information from all the columns which is definitely not good... I'm thinking this project needs to be redone from scratch. The array doesn't seem to want to pick-up any information sent to it.
|
# ? Jan 3, 2010 02:09 |
|
See maybe this is why I'm not a professional developer, but I don't see why you need all of that class stuff when this will do the same thing:php:<?php // list of acceptable pokemon type IDs $poketypes = array(0=>'%', 1=>'grass', 2=>'fire', 3=>'ground'); // get the type and number to select $ptype = (int) $_POST['t1']; $pokemonlimit = abs(intval($_POST['pokemonlimit'])); // if the number is a chosable option, return its database value, otherwise select 'all types' by default $poktype = (isset($poketypes[$ptype])) ? $poketypes[$ptype] : '%'; // run query and return results $query = mysql_query("SELECT * FROM pokemon WHERE `type1` LIKE '".$poktype."' ORDER BY rand() LIMIT ".$pokemonlimit); while ($data = mysql_fetch_assoc($query)) { echo $data['name'].'<br>'; } ?> DoctorScurvy fucked around with this message at 04:16 on Jan 3, 2010 |
# ? Jan 3, 2010 03:28 |
|
Your code makes more sense, but it seems to be missing a some kind of closing here:php:<? $query = mysql_query("SELECT * FROM pokemon WHERE `type` LIKE '".$poktype."' ORDER BY rand() LIMIT ".$pokemonlimit); while ($data = myql_fetch_assoc($query)) { echo $data['name'].'<br>'; } ?>
|
# ? Jan 3, 2010 03:54 |
|
Fluue posted:Your code makes more sense, but it seems to be missing a some kind of closing here:
|
# ? Jan 3, 2010 04:08 |
|
The script works great except for one thing: it doesn't seem to want to take the type into account in the WHERE clause. I'll keep trying out different things with your code, though.
|
# ? Jan 3, 2010 04:29 |
|
Fluue posted:The script works great except for one thing: it doesn't seem to want to take the type into account in the WHERE clause. I'll keep trying out different things with your code, though.
|
# ? Jan 3, 2010 04:33 |
|
Thank you! I seem to have solved the problem. I rechecked all my $_POST []; variables and then saw that I had named my form. I removed the form name and now everything seems to be in working order. Thank you Haywire, Barbarianbob, and DoctorScurvy for helping me work through this script!
|
# ? Jan 3, 2010 04:47 |
|
|
# ? Jun 3, 2024 19:35 |
|
DoctorScurvy posted:See maybe this is why I'm not a professional developer, but I don't see why you need all of that class stuff when this will do the same thing: There are a few inter-related things here, but in general there is no inherent advantage to well designed/structured/written OO code as opposed to equally well designed/structured/written procedural code. Some people do make the mistake of thinking that the OO paradigm in some way 'inherently' produces better designed/structured/written code. They are incorrect; it usually means either that they themselves just better understand how to do OO well than how to do procedural structured code well, or that the ''OO way of thinking' better matches their natural conceptualising of problems. In the specific 'why use PDO?' case, its the fact that it protects your DB from SQL injection. Don't get me wrong, your script was protected as well, but there are drawbacks to your method, particularly regarding URLs and extensibility. Lets take a contrived pokemon example: We'll start with a state where there are 'water' and 'earth' pokemons (bear with me; I was never a pokefan, OK?) In your system, we'd have type 0 as any, type 1 as water and type 2 as earth. Our request URL for water pokemon might be: http://www.example.com/pokemon/bytype/1 or something, presuming we want some nice readable urls. If someone goes for http://www.example.com/pokemon/bytype/99 then they get all the pokemons - as an aside; this is probably not the desired result in that case, as there are in fact NO type 99 pokemons In your code you'll have, as you had, php:<? $valid_types = array(0=>'%', 1=>'water', 2=>'earth'); // plus the pul from POST and lookup code ?> php:<? // this is pseudocode... $query = $db->prepare('SELECT name FROM pokemon WHERE type = :type'); $query->execute(array('type'=>$_POST['type']); ?> http://www.example.com/pokemon/bytype/water and if you send http://www.example.com/pokemon/bytype/madeuptype you will get 'no pokemons of type "madeuptype"' because there aren't any in the DB. Which, as I said, is actually a more sensible result when an invalid type is given than, conceptually; all pokemon are of this invalid type - which is what returning all by default is saying. Note that the URLs for the PDO version ore more meaningful than the /bytype/1 version. Its a small thing, but quite nice in the general scheme of things. Nobody should need to track whether type 63 or type 49 pokemons are the 'cheese' ones... Secondly, and more importantly, six months down the line, they release some lovely new 'wood' pokemon, and you enter them in the DB. Then you can make your links as appropriate, update forms etc. In the mysql_XYZ version, you need to update your array and assign a new pokemon type number to them. In the PDO version, once the DB is updated, and the links / forms are updated the work is DONE, because the actual type string is what drives the code, by adding the data, the existing SQL will look them up just fine. Incidentally, when you don't manually interject an id number to type value lookup, the links and forms can conceivably update themselves, by being built to lookup 'SELECT DISTINCT type FROM pokemon' etc Getting the forms and links that give users access to the data to be driven BY the data is where the magic happens, basically. Its all about using the tool that allows you to do the least work.
|
# ? Jan 3, 2010 13:36 |