rulururu

post trim() Validation

July 15th, 2008

Filed under: Code,PHP — Brenton Alker @ 13:11

While writing a fairly standard sign-up/log-in system, I got to the point of validating the password to make sure it only contained acceptable characters. Now for me this would usually mean a regular expression. But, since this system wasn’t for me I decided to make the “valid characters” configurable, and generally the people configuring it won’t be able to write a regular expression.

My solution is to use php’s trim() (or more specifically rtrim(), but it doesn’t really matter) by passing the users input as the first argument, and the string of valid characters as the second; I should get an empty string in return. So the test, only allowing lowercase letters, becomes:

if ('' !== rtrim($input, 'abcdefghijklmnopqrstuvwxyz')) {
return false;
}

Simple! I don’t know how it compares speed wise with other methods, but it seems simple and effective and I can’t imagine it’s terribly slow unless your strings get larger.

I’m also not sure how it would handle multi-byte characters. But I don’t think it would be a problem if it doesn’t. Anyone got any insight?

post An Attempt at Restricted Auto-Login

July 4th, 2008

Filed under: Code,PHP — Brenton Alker @ 16:33

The system I have been building is a direct marketing system (don’t hate me, it’s opt-in) and to be compliant with their ethics policy it requires “2 click unsubscribe” functionality from all its email campaigns; 1 click on the link in the email, and 1 click on a big “Don’t Ever Send Me Email Again” button. So people actually have an option to opt out at any time, as any good (and I use that word in the relative sense) email marketing system should.

The problem this poses for me though, is how can I let a member unsubscribe without authenticating themselves? Authentication would take more than 2 clicks! The obvious solution is to not require authentication by simply adding the members identifier (email address, user name, database id, whatever can identify them uniquely) to the link. So I would have a URL something like:

http://example.com/member/unsubscribe?member=1234

And simply make all the options on that page act on the member identified by the identifier. Great! Until a malicious netizen comes along, seeing the scheme decides to systematically unsubscribe all my members. This would be achieved easily by guessing the identifiers, made even easier because they’re sequential, and clicking the unsubscribe button.

The initial solution is to use some other “key” in the link, one that is not so easy to guess. So a key was added, giving the url a form like:

http://example.com/member/unsubscribe?member=1234&key=SECRETKEY

Were SECRETKEY is a key they is generated randomly when the user signs up and stored with their account details. Using this, the system would allow a user to log in simply by clicking the link, they could then unsubscribe.

The problem being, of course, that by simply logging the user into their account anyone who has the link has full access to the account without needing to know any more information. So they could then read (and change) email addresses, passwords etc. Not ideal.

My solution has been to take the key a step further, and limit it to only the intended page. In this case the unsubscribe page. So my new url looks like this:

http://example.com/member/unsubscribe?token=1234:HASH

I’ve removed the member variable and combined it into a delimited token. This was mainly because I thought it looked a little nicer, it would function just as well as a separate variable. The real change is in the HASH. The new hash still uses the SECRETKEY that was used in the previous iteration, except it is now combined with the url that I want to give them access to, and an added salt so all tokens can be invalidated if required. In PHP, this looks something like this:

$allowedUrl = '/member/unsubscribe';
$hash = md5($salt . $allowedUrl . ':' . $secretKey);
$token = $id . ':' . $hash;

On the page, to allow access, the token is decomposed, the member’s identifier extracted, and their key retrieved. Their key is then used in the same process to generate a hash for the URL they are requesting. If the hashes match, they have access. To make it a little more user friendly, the allowed URL is added to their session, so they still have access to the page even if they lose the token from the URL. If they hit any other page with the restricted URL in their session they are logged out and sent back to re-authenticate.

One shortfall that I am aware of, and that was pointed out in a discussion on #phpc, is the members secret keys should ideally be rotated, so it can only be used once. This would mean that stealing a link would at worst allow one login, and only to one page. This may be implemented if it doesn’t impact too much on usability (that classic trade-off, security vs. convenience).

Now, I am not a security expert and as such don’t recommend anyone take my advice (is this good enough for a disclaimer), but apart from the one caveat mentioned, I think this solution meets the requirements without forfeiting too much in security. Any comments or advice on the technique are, as always, appreciated.

post Using a MySQL table as a thread-safe queue

April 8th, 2008

Filed under: Code,MySQL — Brenton Alker @ 10:56

As part of the current application I am developing, I have the need for a reliable queue that is not going to allow duplicate reads when popped from multiple threads or processes.

The queue in this case is an outgoing mail queue. The system needs to read the task from the queue, generate the email by substituting in the member’s details, and then send it to the mail server.

When reading the queue with only 1 process, it is easy — read the queue, process the email, delete the queue entry, repeat. Concurrency adds the problem that 2 (or more) processes could read the same email, and we really don’t want an email going to a member 2 or 3 times.

My first solution involved a single "master" thread that would read the queue and delegate the processing to worker threads. While this worked, it was complicated and error prone. After some discussion with some people on #mysql on freenode. I found what should be a suitable database level method.

By selecting the queue entry with the FOR UPDATE modifier, the row is placed under an exclusive lock — the same lock used when a row is being updated, and won’t be allowed to be read until it is updated (N.B. only works when using the INNODB storage engine)

SELECT id, task FROM queue WHERE processing = 0 FOR UPDATE;

The process now has a lock on that row, and it won’t be read by any others. It can then be updated to mark it as "being processed" or deleted from the queue, depending on your needs.

UPDATE queue SET processing = 1 WHERE id = :id;

With the queue entry safely belonging to the thread, it can now take as long as it needs to process. By keeping the time between the SELECT…FOR UPDATE and UPDATE to a minimum the throughput should be increased significantly from the original non-concurrent solution.

ruldrurd
« Previous PageNext Page »
Powered by WordPress, Web Design by Laurentiu Piron Monitored by SiteUpTime
Entries (RSS) and Comments (RSS)