Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
574,73 KB
Nội dung
328
Chapter 13 User Authentication and Session Security
Ironically, a tuned system makes dictionary attacks even easier for the cracker.At a previ-
ous job, I was astounded to discover a cracker executing a dictionary attack at more
than 100 attempts per second. At that rate, he could attempt an entire 50,000-word dic-
tionary in under 10 minutes.
There are two solutions to protecting against password attacks, although neither is ter-
ribly effective:
n
Create “good” passwords.
n
Limit the effectiveness of dictionary attacks.
What is a ”good” password? A good password is one that cannot be guessed easily by
using automated techniques. A “good” password generator might look like this:
function random_password($length=8) {
$str =
‘’;
for($i=0; $i<$length; $i++) {
$str .= chr(rand(48,122));
}
return $str;
}
This generates passwords that consist of random printable ASCII characters.They are also
very difficult to remember.This is the key problem with truly random password genera-
tors: People hate the passwords they generate.The more difficult a password is to remem-
ber, the more likely a person is to put it on a sticky note on his or her monitor or in a
text file or an email message.
A common approach to this problem is to put the burden of good password genera-
tion on the user and enforce it with simple rules.You can allow the user to select his or
her own password but require that password to pass certain tests.The following is a sim-
ple password validator for this scenario:
function good_password($password) {
if(strlen($password) < 8) {
return 0;
}
if(!preg_match(“/\d/”, $password)) {
return 0;
}
if(!preg_match(“/[a-z]/i”, $password)) {
return 0;
}
}
This function requires a password to be at least eight characters long and contain both
letters and numbers.
A more robust function might check to ensure that when the numeric characters are
removed, what is left is not a single dictionary word or that the user’s name or address is
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
329
Registering Users
not contained in the password.This approach to the problems is one of the key tenets of
consulting work:When a problem is difficult, make it someone else’s problem.
Generating a secure password that a user can be happy with is difficult. It is much easier
to detect a bad password and prevent the user from choosing it.
The next challenge is to prevent dictionary attacks against the authentication system.
Given free reign, a cracker running a dictionary attack will always compromise users.
No matter how good your rules for preventing bad passwords, the space of human-
comprehensible passwords is small.
One solution is to lock down an account if it has a number of consecutive failures
against it.This solution is easy enough to implement.You can modify the original
check_credentials function to only allow for a fixed number of failures before the
account is locked:
function check_credentials($name, $password) {
$dbh = new DB_Mysql_Prod();
$cur = $dbh->execute(
“
SELECT
userid, password
FROM
users
WHERE
username = ‘$name’
AND failures < 3”);
$row = $cur->fetch_assoc();
if($row) {
if($password == $row[
‘password’]) {
return $row[‘userid’];
}
else {
$cur = $dbh->execute(“
UPDATE
users
SET
failures = failures + 1,
last_failure = now()
WHERE
username =
‘$name’”);
}
}
throw new AuthException(“user is not authorized”);
}
Clearing these locks can either be done manually or through a cron job that resets the
failure count on any row that is more than an hour old.
The major drawback of this method is that it allows a cracker to disable access to a
person’s account by intentionally logging in with bad passwords.You can attempt to tie
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
330
Chapter 13 User Authentication and Session Security
login failures to IP addresses to partially rectify this concern. Login security is an endless
battle.There is no such thing as an exploit-free system. It’s important to weigh the
potential risks against the time and resources necessary to handle a potential exploit.
The particular strategy you use can be as complex as you like. Some examples are no
more than three login attempts in one minute and no more than 20 login attempts in
a day.
Protecting Passwords Against Social Engineering
Although it’s not really a technical issue, we would be remiss to talk about login security
without mentioning social engineering attacks. Social engineering involves tricking a user
into giving you information, often by posing as a trusted figure. Common social engi-
neering exploits include the following:
n
Posing as a systems administrator for the site and sending email messages that ask
users for their passwords for “security reasons”
n
Creating a mirror image of the site login page and tricking users into attempting
to log in
n
Trying some combination of the two
It might seem implausible that users would fall for these techniques, but they are very
common. Searching Google for scams involving eBay turns up a plethora of such
exploits.
It is very hard to protect against social engineering attacks.The crux of the problem is
that they are really not technical attacks at all; they are simply attacks that involve duping
users into making stupid choices.The only options are to educate users on how and why
you might contact them and to try to instill in users a healthy skepticism about relin-
quishing their personal information.
Good luck, you’ll need it.
JavaScript Is a Tool of Evil
The following sections talk about a number of session security methods that involve cookies. Be aware that
client-side scripting languages such as JavaScript have access to users’ cookies. If you run a site that allows
users to embed arbitrary JavaScript or CSS in a page that is being served by your domain (that is, a domain
that has access to your cookies), your cookies can easily be hijacked. JavaScript is a community-site crack-
er’s dream because it allows for easy manipulation of all the data you send to the client.
This category of attack is known as cross-site scripting. In a cross-site scripting attack, a malicious user uses
some sort of client-side technology (most commonly JavaScript, Flash, and CSS) to cause you to download
malicious code from a site other than the one you think you are visiting.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
331
Maintaining Authentication: Ensuring That You Are Still Talking to the Same Person
Maintaining Authentication: Ensuring That You
Are Still Talking to the Same Person
Trying to create a sitewide authentication and/or authorization system without cookies
is like cooking without utensils. It can be done to prove a point, but it makes life signifi-
cantly harder and your query strings much uglier. It is very difficult to surf the Web
these days without cookies enabled.All modern browsers, including the purely text-
based ones, support cookies. Cookies provide sufficient benefit that it is worth not sup-
porting users who refuse to use them.
A conversation about ways to tie state between requests is incomplete without a dis-
cussion of the pitfalls.The following sections cover commonly utilized but flawed and
ineffective ways to maintain state between requests.
Checking That $_SERVER[REMOTE_IP] Stays the Same
Relying on a user’s IP address to remain constant throughout his or her session is a clas-
sic pitfall; an attribute that many people think stays constant across requests as the user’s
Internet connection remains up. In reality, this method yields both false-positives and
false-negatives. Many ISPs use proxy servers to aggressively buffer HTTP requests to
minimize the number of requests for common objects. If you and I are using the same
ISP and we both request foo.jpg from a site, only the first request actually leaves the
ISP’s network.This saves considerable bandwidth, and bandwidth is money.
Many ISPs scale their services by using clusters of proxy servers.When you surf the
Web, subsequent requests may go through different proxies, even if the requests are only
seconds apart.To the Web server, this means that the requests come from different IP
addresses, meaning that a user’s $_SERVER[‘REMOTE_IP’] address can (validly) change
over the course of a session.You can easily witness this behavior if you inspect inbound
traffic from users on any of the major dial-up services.
The false-negative renders this comparison useless, but it’s worth noting the false-
positive as well. Multiple users coming from behind the same proxy server have the same
$_SERVER[‘REMOTE_IP’] setting.This also holds true for users who come through the
same network translation box (which is typical of many corporate setups).
Ensuring That $_SERVER[‘USER_AGENT’] Stays the Same
$_SERVER[‘USER_AGENT’] returns the string that the browser identifies itself with in the
request. For example, this is the browser string for my browser:
Mozilla/4.0 (compatible; MSIE 5.21; Mac_PowerPC)
which is Internet Explorer 5.2 for Mac OS X. In discussions about how to make PHP
sessions more secure, a proposal has come up a number of times to check that
$_SERVER[‘USER_AGENT’] stays the same for a user across subsequent requests.
Unfortunately, this falls victim to the same problem as $_SERVER[‘REMOTE_IP’]. Many
ISP proxy clusters cause different User Agent strings to be returned across multiple
requests.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
332
Chapter 13 User Authentication and Session Security
Using Unencrypted Cookies
Using unencrypted cookies to store user identity and authentication information is like a
bar accepting hand-written vouchers for patrons’ ages. Cookies are trivial for a user to
inspect and alter, so it is important that the data in the cookie be stored in a format in
which the user can’t intelligently change its meaning. (You’ll learn more on this later in
this chapter.)
Things You Should Do
Now that we’ve discussed things we should not use for authentication, let’s examine
things that are good to include.
Using Encryption
Any cookie data that you do not want a user to be able to see or alter should be
encrypted.
No matter how often the warning is given, there are always programmers who
choose to implement their own encryption algorithms. Don’t. Implementing your own
encryption algorithm is like building your own rocket ship. It won’t work out.Time and
again, it has been demonstrated that homegrown encryption techniques (even those
engineered by large companies) are insecure. Don’t be the next case to prove this rule.
Stick with peer-reviewed, open, proven algorithms.
The mcrypt extension provides access to a large number of proven cryptographic
algorithms. Because you need to have both the encryption and decryption keys on the
Web server (so you can both read and write cookies), there is no value in using an asym-
metric algorithm.The examples here use the blowfish algorithm; but it is easy to shift to
an alternative cipher.
Using Expiration Logic
You have two choices for expiring an authentication: expiration on every use and expi-
ration after some period of time.
Expiration on Every Request
Expiration on every request works similarly to TCP. A sequence is initiated for every
user, and the current value is set in a cookie.When the user makes a subsequent request,
that sequence value is compared against the last one sent. If the two match, the request is
authenticated.The next sequence number is then generated, and the process repeats.
Expiration on every request makes hijacking a session difficult but nowhere near
impossible. If I intercept the server response back to you and reply by using that cookie
before you do, I have successfully hijacked your session.This might sound unlikely, but
where there is a gain to be had, there are people who will try to exploit the technology.
Unfortunately, security and usability are often in conflict with one another. Creating a
session server that cannot be hijacked is close to impossible.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
333
Maintaining Authentication: Ensuring That You Are Still Talking to the Same Person
Using a sequence to generate tokens and changing them on every request also
consumes significant resources. Not only is there the overhead of decrypting and re-
encrypting the cookie on every request (which is significant), you also need a means to
store the current sequence number for each user to validate their requests. In a multi-
server environment, this needs to be done in a database.That overhead can be very high.
For the marginal protection it affords, this expiration scheme is not worth the trouble.
Expiration After a Fixed Time
The second option for expiring an authentication is to expire each cookie every few
minutes.Think of it as the time window on the lift ticket.The pass works for an entire
day without reissue.You can write the time of issuance in the cookie and then validate
the session against that time.This still offers marginal hijack protection because the
cookie must be used within a few minutes of its creation. In addition, you gain the fol-
lowing:
n
No need for centralized validation—As long as the clocks on all machines are
kept in sync, each cookie can be verified without checking any central authority.
n
Reissue cookies infrequently—Because the cookie is good for a period of time,
you do not need to reissue it on every request.This means that you can eliminate
half of the cryptographic work on almost every request.
Collecting User Identity Information
This is hard to forget but still important to mention:You need to know who a cookie
authenticates. A nonambiguous, permanent identifier is best. If you also associate a
sequence number with a user, that works as well.
Collecting Versioning Information
A small point to note: Any sort of persistent information you expect a client to give
back to you should contain version tags.Without versioning information in your cook-
ies, it is impossible to change cookie formats without causing an interruption of service.
At best, a change in cookie format will cause everyone surfing the site to have to log in
again. At worst, it can cause chronic and hard-to-debug problems in the case where a
single machine is running an outdated version of the cookie code. Lack of versioning
information leads to brittle code.
Logging Out
This is not a part of the cookie itself, but it’s a required feature:The user needs to be
able to end his or her session. Being able to log out is a critical privacy issue.You can
implement the logout functionality by clearing the session cookie.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
334
Chapter 13 User Authentication and Session Security
A Sample Authentication Implementation
Enough talk. Let’s write some code! First you need to settle on a cookie format. Based
on the information in this chapter, you decide that what you want would be fulfilled by
the version number $version, issuance timestamp $created, and user’s user ID
$userid:
<?php
require_once ‘Exception.inc’;
class AuthException extends Exception {}
class Cookie {
private $created;
private $userid;
private $version;
// our mcrypt handle
private $td;
// mcrypt information
static $cypher = ‘blowfish’;
static $mode =
‘cfb’;
static $key =
‘choose a better key’;
// cookie format information
static $cookiename =
‘USERAUTH’;
static $myversion = ‘1’;
// when to expire the cookie
static $expiration = ‘600’;
// when to reissue the cookie
static $warning =
‘300’;
static $glue =
‘|’;
public function _ _construct($userid = false) {
$this->td = mcrypt_module_open ($cypher,
‘’, $mode, ‘’);
if($userid) {
$this->userid = $userid;
return;
}
else {
if(array_key_exists(self::$cookiename, $_COOKIE)) {
$buffer = $this->_unpackage($_COOKIE[self::$cookiename]);
}
else {
throw new AuthException(
“No Cookie”);
}
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
335
Maintaining Authentication: Ensuring That You Are Still Talking to the Same Person
}
}
public function set() {
$cookie = $this->_package();
set_cookie(self::$cookiename, $cookie);
}
public function validate() {
if(!$this->version || !$this->created || !$this->userid) {
throw new AuthException(“Malformed cookie”);
}
if ($this->version != self::$myversion) {
throw new AuthException(“Version mismatch”);
}
if (time() - $this->created > self::$expiration) {
throw new AuthException(“Cookie expired”);
} else if ( time() - $this->created > self::$resettime) {
$this->set();
}
}
public function logout() {
set_cookie(self::$cookiename, “”, 0);
}
private function _package() {
$parts = array(self::$myversion, time(), $this->userid);
$cookie = implode($glue, $parts);
return $this->_encrypt($cookie);
}
private function _unpackage($cookie) {
$buffer = $this->_decrypt($cookie);
list($this->version, $this->created, $this->userid) =
explode($glue, $buffer);
if($this->version != self::$myversion ||
!$this->created ||
!$this->userid)
{
throw new AuthException();
}
}
private function _encrypt($plaintext) {
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
mcrypt_generic_init ($this->td, $this->key, $iv);
$crypttext = mcrypt_generic ($this->td, $plaintext);
mcrypt_generic_deinit ($this->td);
return $iv.$crypttext;
}
private function _decrypt($crypttext) {
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
336
Chapter 13 User Authentication and Session Security
$ivsize = mcrypt_get_iv_size($this->td);
$iv = substr($crypttext, 0, $ivsize);
$crypttext = substr($crypttext, $ivsize);
mcrypt_generic_init ($this->td, $this->key, $iv);
$plaintext = mdecrypt_generic ($this->td, $crypttext);
mcrypt_generic_deinit ($this->td);
return $plaintext;
}
private function _reissue() {
$this->created = time();
}
}
?>
This is a relatively complex class, so let’s start by examining its public interface. If
Cookie’s constructor is not passed a user ID, it assumes that you are trying to read from
the environment; so it attempts to read in and process the cookie from $_COOKIE.The
cookie stored as $cookiename (in this case, USERAUTH). If anything goes wrong with
accessing or decrypting the cookie, the constructor throws an AuthException exception.
AuthException is a simple wrapper around the generic Exception class:
class AuthException extends Exception {}
You can rely on exceptions to handle all our authentication errors.
After you instantiate a cookie from the environment, you might want to call
validate() on it. validate() checks the structure of the cookie and verifies that it is
the correct version and is not stale. (It is stale if it was created more than
$expiration
seconds ago.) validate() also handles resetting the cookie if it is getting close to expi-
ration (that is, if it was created more than
$warning seconds ago). If you instantiate a
cookie with a user ID, then the class assumes that you are creating a brand new
Cookie
object, so validation of an existing cookie isn’t required.
The public method set assembles, encrypts, and sets the cookie.You need this to allow
cookies to be created initially. Note that you do not set an expiration time in the cookie:
set_cookie(self::$cookiename, $cookie);
This indicates that the browser should discard the cookie automatically when it is shut
down.
Finally, the method logout clears the cookie by setting it to an empty value, with an
expiration time of 0. Cookie expiration time is represented as a Unix timestamp, so 0 is
7pm Dec 31, 1969.
Internally, you have some helper functions. _package and _unpackage use implode
and explode to turn the array of required information into a string and vice versa.
_encrypt and _decrypt handle all the cryptography. _encrypt encrypts a plain-text
string by using the cipher you specified in the class attributes (blowfish). Conversely,
_decrypt decrypts an encrypted string and returns it.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
337
Maintaining Authentication: Ensuring That You Are Still Talking to the Same Person
An important aspect to note is that you use this:
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
to create the “initial vector,” or seed, for the cryptographic functions.You then prepend
this to the encrypted string. It is possible to specify your own initial vector, and many
developers mistakenly choose to fix both their key and their initial vector in their crypto
libraries.When using a symmetric cipher with a fixed key in CBC (Cypher Block
Chaining), CFB (Cypher Feedback), or OFB (Output Feedback) mode, it is critical to
use a random initial vector; otherwise, your cookies are open to cryptographic attack.
This is absolutely critical in CFB and OFB modes and somewhat less so in CBF mode.
To utilize your library, you wrap it in a function that you call at the top of every
page:
function check_auth() {
try {
$cookie = new Cookie();
$cookie->validate();
}
catch (AuthException $e) {
header(“Location: /login.php?originating_uri=”.$_SERVER[‘REQUEST_URI’]);
exit;
}
}
If the user’s cookie is valid, the user continues on; if the cookie is not valid, the user is
redirected to the login page.
If the user’s cookie does not exist or if there are any problems with validating it, the
user is issued an immediate redirect to the login page.You set the
$_GET variable
originating_uri so that you can return the user to the source page.
login.php is a simple form page that allows the user to submit his or her username
and password. If this login is successful, the user’s session cookie is set and the user is
returned to the page he or she originated from:
<?php
require_once
‘Cookie.inc’;
require_once
‘Authentication.inc’;
require_once ‘Exception.inc’;
$name = $_POST[‘name’];
$password = $_POST[‘password’];
$uri = $_REQUEST[‘originating_uri’];
if(!$uri) {
$uri = ‘/’;
}
try {
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
[...]... enable it by using the following php. ini setting: session.use_trans_sid=1 In this setting, trans_sid stands for “transparent session ID,” and it is so named because tags are automatically rewritten when it is enabled For example, when use_trans_id is true, the following: < ?php session_start(); ?> Foo will be rendered as this: foo Using... increasingly important Further Reading You can find a good introduction to using HTTP Basic Authentication in PHP in Luke Welling and Laura Thomson’s PHP and MySQL Web Development.The standard for Basic Authentication is set in RFC 2617 (www.ietf.org/rfc/rfc2617.txt) The explanation of using cookies in the PHP online manual is quite thorough, but if you have unanswered questions, you can check out RFC 2109... different architectures.The session handler to be used is set with this php. ini setting: session.save_handler=’files’ PHP has two prefabricated session handlers: files—The default, files uses an individual file for storing each session mm—This is an implementation that uses BSD shared memory, available only if you have libmm installed and build PHP by using the –with-mm configure flag n n We’ve looked at methods... that cart throughout their entire session PHP offers no data persistence between requests, so you need to tuck this data away someplace where you can access it after the current request is complete There are a number of ways to track state.You can use cookies, query string munging, DBM-based session caches, RDBMS-backed caches, application server–based caches, PHP s internal session tools, or something... your php. ini file It would be trivial for a user to alter his or her own cookie to change any of these values In this example, that would serve no purpose; but in most applications you do not want a user to be able to alter his or her own state.Thus, you should always encrypt session data when you use client-side sessions.The encryption functions from Chapter 13 will work fine for this purpose: < ?php. .. scalability breaks down Still, it is good to know these things and design with all the potential limitations in mind PHP Sessions and Reinventing the Wheel While writing this chapter, I will admit that I have vacillated a number of times on whether to focus on custom session management or PHP s session extension I have often preferred to reinvent the wheel (under the guise of self-education) rather than... features I would rather implement myself and those that I would prefer to use out of the box PHP sessions are very robust, and while the default session handlers fail to meet a number of my needs, the ability to set custom handlers enables us to address most of the deficits I find The following sections focus on PHP s session extension for lightweight sessions Let’s start by reviewing basic use of the... cookies method uses a dedicated cookie to manage the session ID By default the name of the cookie is PHPSESSIONID, and it is a session cookie (that is, it has an expiration time of 0, meaning that it is destroyed when the browser is shut down) Cookie support is enabled by setting the following in your php. ini file (it defaults to on): session.use_cookies=1 The query string munging method works by automatically... forward the user to the authentication server, passing in the current URL so the user can be returned to the right place when authentication is complete signon .php on the authentication server is similar to the login page you put together earlier: < ?php require_once ‘Cookie.inc’; require_once ‘SingleSignOn.inc’; $name = $_POST[‘name’]; $password = $_POST[‘password’]; $request = $_REQUEST[‘request’]; try... string is ugly and produces cryptic-looking URLs For both cookie- and query-managed session identifiers, the name of the session identifier can be set with the php. ini parameter session.name For example, to use MYSESSIONID as the cookie name instead of PHPSESSIONID, you can simply set this: session.name=MYSESSIONID In addition, the following parameters are useful for configuring cookie-based session support: . dictionary word or that the user’s name or address is
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
329
Registering Users
not contained. logging in with bad passwords.You can attempt to tie
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
330
Chapter 13 User Authentication