Using a Working Email Address for Identity Verification Many online applications demand possession of a valid email address as a condition of membership, imagining it to be a proof of i
Trang 1C H A P T E R 1 7 ■ A L L O W I N G O N L Y H U M A N U S E R S 343
Figure 17-6 The generated captcha
3 Place the Captcha Image in a Form
To create the challenge, all you need to do is place the captcha image in an HTML form, and
provide a text input box for the user’s response, along with some basic instructions The code
for offering the challenge follows, and can be found also as TRaeTYR7`c^aYa in the Chapter 17
folder of the downloadable archive of code for Pro PHP Security at Yeea+ hhhRacVddT`^
This is a standard HTML form, with PHP needed only to specify the form action The source for
the image is the preceding script that generates the captcha
4 Check the User’s Response
When the user submits the form, you compare his answer to PD6DD:@?LTRaeTYRPh`cUN, and
if it matches, then you can be reasonably certain that he is a human The code for retrieving the
user’s response and comparing it to the correct response follows, and can be found also as
TRaeTYR4YVT\aYa in the Chapter 17 folder of the downloadable archive of code for Pro PHP
Security at Yeea+ hhhRacVddT`^
Trang 2Attacks on Captcha Challenges
Malicious attackers have not stood idly by as programmers have imposed captcha challenges
to prevent or minimize abuse Obviously some effort, often some considerable effort, must be expended to attack a captcha in a way that is likely to be successful But if the payoff is great enough, then the effort is worthwhile for the attacker Among the direct attacks upon captchas that have been developed are these:
• Brute force attacks might begin with simple guessing and range all the way up to running
through every entry in a dictionary These attacks can be surprisingly effective if the challenge involves reproducing an actual word This is particularly true if your source for the words is the same unix dictionary that is available to the attacker, at fdc dYRcV UZTe h`cUd As we said earlier, you might make such an attack upon a real word harder
by somehow hashing or encrypting the word, but in that case there is little point in using
a real word
• Attackers may use artificial intelligence techniques to analyze a challenge’s requirements,
even if only to narrow the range of possible answers to the point where brute force guessing
is likely to be successful Existing object recognition routines (developed, for example, for face recognition applications) can be used to attempt to recognize even distorted letters and numbers Sound recognition routines (originally intended to support voice recognition) can be easily used for attempting to recognize a challenge word
• Finally, hijacking attacks are very effective, because they eliminate the need for the
attacker to process the captcha at all Faced with answering a captcha challenge, the hijacker arranges an automated situation in which she can present the same challenge
to a human user in another setting For example, a spammer wishing to register for free email accounts might create a “free internet porn” website and advertise it using her own spam engine When a user shows up to the porn site, the registration script initiates
an email registration, on behalf of the spammer, in the background It then presents the email system’s captcha to the user, as a condition of access to the porn site The human
SnyderSouthwell_5084.book Page 344 Wednesday, July 27, 2005 9:37 PM
Trang 3C H A P T E R 1 7 ■ A L L O W I N G O N L Y H U M A N U S E R S 345
user provides the correct answer, which is sent back to the email site to gain access This
sort of challenge proxying is an excellent example of how a clever and unpredictable
human response can defeat what seems like strong security
Potential Problems in Using Captchas
We have shown, we hope, that, with PHP’s help, using captchas is not terribly difficult But
there are potential problems
Hijacking Captchas Is Relatively Easy
An enterprising coder could build a site that proxies your captcha in a matter of hours If she
can get 50,000 people to look at her site and provide the answer to each captcha, she can prove
that her script is human 50,000 times If the point of using a captcha is to prevent someone
from scripting the use of your site, you will need other defenses as well We will discuss some of
these in Chapter 18
The More Captchas Are Used, the Better AI Attack Scripts Get at
Reading Them
Most of what is public information about AI attacks upon captchas is academic; as one group
of researchers develops a more difficult captcha, another group tries to find ways to defeat it—
and often succeeds There is no reason to imagine that the situation is any different in the
nonacademic world, although spammers (unlike professors) are not typically talking about
their successes When the rewards are high enough, someone will make the effort to break the
challenge What this really means for you as a programmer is that no high-stakes challenge you
develop is likely to be successful for very long For that reason, you should monitor usage of
your website carefully, examining log files to see to what extent users successfully pass through
your captcha challenges, and whether they go where you expect them to You should also be
sure to update your challenges as better versions become available
Generating Captchas Requires Time and Memory
Even the simplest captcha challenges require some machine effort to deliver: database accesses
and image creation at the least While one instance of captcha generation may not require
much machine effort, if your website is a busy one, so that hundreds of generation requests
might need to be processed every second, the burden can become noticeable The resulting
delays could drive users away You may actually need to upgrade or supplement your hardware
if this is a problem for you
Captchas That Are Too Complex May Be Unreadable by Humans
The concept of distorting an image in order to make the text in that image more difficult to
recognize is simple enough; what is hard is to know where to stop An image that is difficult for
a machine to interpret may not be so difficult for a human—or it may The fact that you as a
programmer can recognize the text contained in a distorted image, text that you already know,
is no guarantee that your mother or your neighbor or the person in the next town can read it
SnyderSouthwell_5084.book Page 345 Wednesday, July 27, 2005 9:37 PM
Trang 4346 C H A P T E R 1 7 ■ A L L O W I N G O N L Y H U M A N U S E R S
There can be a very fine line between making a captcha easy enough to include humans and hard enough to exclude machines Again, you need to monitor what is happening to your website, and if necessary adjust the complexity of your captchas Another alternative, especially if you are a bit nervous about how difficult your captchas are, might be to allow a second try, or a second try if some of the letters are correct But if an application is sensitive enough to protect with a captcha, then in general we recommend that you not be generous in allowing retries
As a compromise, you could provide an easy way for users to request another (and fore different) captcha on the initial form if they can’t read the first one, rather than allowing them to retry after the fact
there-Even Relatively Straightforward Captchas May Fall Prey to Unforeseeable User Difficulties
One completely unknown factor in every online application is the user’s capabilities Even when the user is in fact an actual human rather than an attacking machine, or perhaps espe-cially when the user is a human, unanticipated insufficiencies or difficulties on the user’s end may get in the way of a successful response to even the simplest captcha challenge A user with
a visual disability or deficiency is likely to have little or no chance of fulfilling a visual captcha challenge; one with an aural disability or deficiency, or with missing or malfunctioning audio software or hardware, is similarly handicapped when presented with an audio captcha As a programmer, you need to avoid falling into the trap of assuming that even a well-crafted captcha challenge will automatically succeed in allowing a human user to qualify As a safety device, to improve the chances for success, you should at least offer alternatives so that accidents of user capabilities do not automatically disqualify legitimate users
Summary
In this chapter, we have discussed captchas, challenges that require the user to exercise some
sort of intellectual judgment before being permitted to continue; they are designed to block robots or automated attackers from continuing Captchas might require reading obfuscated text contained in an image, hearing obfuscated speech, or interpreting a set of conditions
We demonstrated how to create and use a simple text image captcha Finally, we outlined the problems inherent in using captchas and expecting them to discriminate reliably between human and machine respondents
In Chapter 18, we will continue with the next problem in practicing secure operations: now that you know that your users are human, how do you go about verifying their identities?
SnyderSouthwell_5084.book Page 346 Wednesday, July 27, 2005 9:37 PM
Trang 5In the last chapter, we discussed attempting to prove that your users are human In this chapter,
we will attempt to determine just who those human users are, so that you can prevent them
from abusing your application
We are particularly interested in this chapter in online applications through which users
interact with each other in a community or collaborative context Examples of such behavior
include posting comments or reviews, engaging in discussion about an issue or document, or
creating and sharing online content such as photo albums or wiki pages These applications
depend to a large degree on mutual trust and acceptance of a social contract between the
participants In large-scale or commercial applications, behavior is often codified in a Terms of
Service document or an Acceptable Use Policy Smaller communities rely on common netiquette
and social norms that may or may not actually be written down, but must still be enforceable
should the need arise
Inevitably, in a successful community, the need will arise Human nature ensures that for
every few brilliant or exceptionally interesting members of an online community, there will be
somebody who is just there to spoil the party You can suspend the account of a problem user,
of course, but he may just see this as a challenge and attempt to re-register under one or more
new identities Identity verification is also problem in applications where the stakes for abuse
are high, as in e-commerce transactions and online voting If a single user can fool these
appli-cations with multiple identities, then she can perpetrate large-scale fraud and quickly devalue
the trust that other users invest in the application
Identity Verification
The problem of identity verification is particularly difficult for online communities, since they
typically have a large and geographically diverse user base The problem is exacerbated for
applications that allow new users to register via a public form This makes it impractical to
research the identity of each individual applicant before granting access Abusers can remain
essentially anonymous Furthermore, a single problem user can, with a little work and the use
of anonymizing proxies or botnets (networks of robot machines, engaging in automated
attempts at various kinds of attacks; see http://en.wikipedia.org/wiki/Botnet for more
infor-mation), register under a large number of different pseudonyms, each appearing to come from
a different ISP
SnyderSouthwell_5084C18.fm Page 347 Wednesday, July 20, 2005 5:17 AM
Trang 6348 C H A P T E R 1 8 ■ V E R I F Y I N G Y O U R U S E R S ’ I D E N T I T I E S
There are ways to profile or to screen potential users (based on geography, choice of proxy,
or answers to questions on the registration form) But there is no good way to avoid in advance the mistake of allowing an apparently legitimate user to register, who then becomes a problem later on
However, identity verification can protect you from making the same mistake twice If a registrant can be positively identified as someone who has not acted responsibly in the past, then she can be denied a new account To the extent that you make it difficult to assume a bogus identity in your application, you can prevent someone from repeatedly abusing your application or harassing your users
Suppose that a user begins making unwelcome advances to a sales representative whose job is monitoring your company’s sales and support message board You would probably take immediate steps to invalidate the user’s account and hide (but not delete; you want to keep them as evidence) the offending posts If the user was really just trying his luck at getting a date,
he will get the message that such behavior is not appropriate and move on
But if the user was being disruptive on purpose, he will simply register again under a different identity, and either continue posting messages in the same vein, or move on to some other sort of mischief
Thus, being able to positively associate a user with an identity, or at least making it difficult
to forge multiple identities, is essential to the overall security and usability of your application
Who Are the Abusers?
If you have not managed a publicly available application or service that is subject to such abuse, you may be wondering just who these problem users are The full spectrum of abusers can, we believe, be grouped into three categories, based on their motives for acting against the generally accepted norms of online behavior
Spammers
To date, the most prominent form of identity abuse has come from users trying to market a product or service, or trying to increase their sites’ search engine rankings by sowing links on other sites The activities of a spammer might include the following:
• Posting advertisements
• Posting bogus product reviews or other commercial spin for their own products or against a competitor
• Starting pyramid schemes
• Selling graymarket products such as pharmaceuticals, software, or adult servicesThe primary motive of spammers is commercial, and so it is relatively easy to prevent them by charging a modest fee for access to the system Once the fee for access begins to cut into the expected return from posting advertisements on your system, spammers will either move on,
or apply to become legitimate advertisers on your site
SnyderSouthwell_5084C18.fm Page 348 Wednesday, July 20, 2005 5:17 AM
Trang 7C H A P T E R 1 8 ■ V E R I F Y I N G Y O U R U S E R S ’ I D E N T I T I E S 349
MAKING COMMENT SPAM LESS ATTRACTIVE
Some spammers will post links to their sites in comments on your site, in order to make search engines think
that you are linking to them In competitive search categories like online gambling and retailing, having a link
on many other sites can improve a spammer's ranking
This behavior can be deterred by telling the major search engine indexers to ignore any links in the
comments on your site Ever since the HTML 4.01 specification (dated 24 December 1999), the <a> anchor
tag has been permitted to contain a rel attribute that defines link types A list of recognized link types is
provided, but in addition, authors are permitted “to define additional link types not described in this specification.”
Accordingly, led by Google, the big search engine operators have promoted the use of a rel="nofollow"
attribute, which is interpreted by search engines as forbidding the inclusion of a link so marked in their indexes
Adding this new attribute to any submitted <a> tag will reduce the attractions of comment spamming, especially for
low-traffic sites where the spammers aren’t getting many hits anyway for their efforts On high-traffic sites,
however, there are plenty of reasons beyond search rankings for spammers to attempt to ply their trade For
more information on the rel="nofollow" attribute, see relevant parts of the W3C’s HTML 4.01
specifica-tions at http://www.w3.org/TR/html4/struct/links.html#h-12.2 and http://www.w3.org/TR/
html4/types.html#type-links, and Google’s original blog announcement at http://googleblog
blogspot.com/2005/01/preventing-comment-spam.html
Scammers
The anonymity of online services is attractive to those who fancy being able to get away with
something that is illegal or immoral Scammers use your application to do things that they
wouldn’t do on their own servers, hoping that you rather than they will be the target of any
legal actions Here are some examples of this kind of behavior:
• Posting any sort of large or popular file to avoid having to pay bandwidth fees
• Posting pornographic material to avoid laws forbidding such posting
• Posting copyrighted material such as music or software to avoid intellectual property laws
• Conning other users into donating money to bogus causes
• Soliciting other potential spammers or scammers
Scammers often have a strong financial incentive for doing what they do, so the adoption of a
registration fee may have little effect You may think that payment of such a fee could be used
to trace a scammer’s real identity, but it is likely that anyone attempting to pull off a serious con
or crime will have access to stolen credit cards or funding sources
On the other hand, since a scammer’s primary motivation is to avoid being caught, the
threat of surveillance or an in-depth investigation into suspicious registration requests can be
a strong deterrent
SnyderSouthwell_5084C18.fm Page 349 Wednesday, July 20, 2005 5:17 AM
Trang 8350 C H A P T E R 1 8 ■ V E R I F Y I N G Y O U R U S E R S ’ I D E N T I T I E S
Griefers and Trolls
Seemingly worse than spammers and scammers, because of the psychological effect they have
on other users of an application, are people who enjoy annoying or harrassing others So-called
trolls attempt to catch the attention of other users by posting obviously erroneous or
inflam-matory messages Griefers attempt to disrupt an online community through psychological
abuse and off-color postings Here are just of few of the tactics used by these individuals:
• Posting insults or profanity
• Posting slanderous or defamatory material
• Posting objectionable or inappropriate content, such as hate speech or disturbing images
• Habitually flaming other users (escalating arguments)
• Decreasing the signal-to-noise ratio with off-topic posts
• Bullying other members
Because they thrive on attention, attempting to stop trolls from abusing an application can start a vicious circle of increased abuse The best strategy for making a troll go away is to ignore him Therein lies a dilemma, and a sometimes delicate situation: how do you prevent a deter-mined creep from annoying your users, without just egging him on? A satisfied troll will always find a more clever way of annoying you
The problem is compounded by the fact that in all but the most extreme cases, trolls are doing nothing illegal Imagine going to the police with your tales of posted profanity and abuse; they are likely to shrug their shoulders at your dilemma The aim of trolls and griefers is,
in fact, to attract other users’ attention onto themselves, without upsetting anyone to the point
of taking real-world action
Using a Working Email Address for Identity
Verification
Many online applications demand possession of a valid email address as a condition of membership, imagining it to be a proof of identity But it is trivially easy to make up a valid email address, and having a valid email address should never be confused with having a working email address A user with an actual working email address is thought to be findable.Even though the number of email addresses is infinite, the number of domain names is finite, and domains are registered to identifiable entities The name and address of a mailbox provider, an Internet Service Provider (ISP), or an organization can be determined simply by looking at domain registration records Since most ISPs are not in the business of handing out free or anonymous mailboxes, it is generally assumed that the identity of a problem user can be tracked down via the mailbox provider
Experience has shown us that this is not always the case, since it is not difficult to obtain any number of semi-anonymous mailboxes (via mass mailbox providers like Hotmail or Yahoo, via your own domain name, or even via stealing access to other people’s mailboxes) Still, a user’s possession of a working mailbox at a reputable ISP does usually provide some channel for communicating reliably with him Some problem users can be dissuaded from their abuse
SnyderSouthwell_5084C18.fm Page 350 Wednesday, July 20, 2005 5:17 AM
Trang 9C H A P T E R 1 8 ■ V E R I F Y I N G Y O U R U S E R S ’ I D E N T I T I E S 351
through persuasion, gentle or otherwise, and it is important to try plain old communication
before taking stronger measures to correct abusive behavior Having a verified email address
with which to attempt such communication is therefore important, and is certainly a minimum
requirement under an application’s Terms of Service
Verify the Working Mailbox
It is possible, with some (but certainly not all) mail servers, to verify the existence of a recipient,
without actually taking the time to send a message You can do this yourself from a shell
prompt, with the following series of just three commands:
Connection closed by foreign host
You connect to the default mailserver port of 25 on the host, and get back a response code of
220 if the connection is successful You issue the VRFY command with the email address that
you want to verify The mailserver will reply with a response code of 252 if the mailbox exists,
and some other code if not Finally, you issue a QUIT command and the host responds with a
code of 221 that the connection has been closed
Before issuing the VRFY command, you might have issued an EHLO (for Extended Hello)
command, which is supposed to cause the server to return a list of extended SMTP commands
implemented by the server If the VRFY command is not in the list, then this technique might
not work However, the list returned is not always reliable, and you should not assume that
VRFY will not work just because it is not in that list
An even more important practical matter is that many large mailhosts are starting to
refuse to positively identify their active mailboxes, in order to protect the identities of their
users and to prevent the automated verification of addresses on spam lists (after all, a spammer
can be much more efficient if she sends messages to verified recipients only) Before too long,
most mailhosts either will not implement the VRFY command at all, or they will verify any mailbox
name, saying something like, “Try sending some mail, and I’ll do my best to deliver it.” So this
technique is, as we write, losing its ability to provide useful information
Verifying Receipt with a Token
There is an inherent flaw in the logic of the preceding solution, anyway, if what you really want
to do is verify that a specific applicant is the owner of a specific email address After all, an
abuser could submit any working email address to the preceding routine, and be approved
For these reasons, you need a better way to determine whether the applicant really does
have a working mailbox One extremely reliable way to do this is to send a secret value to the
email address he provides, and ask him to send it back to your application in order to advance
SnyderSouthwell_5084C18.fm Page 351 Wednesday, July 20, 2005 5:17 AM
Trang 10352 C H A P T E R 1 8 ■ V E R I F Y I N G Y O U R U S E R S ’ I D E N T I T I E S
the membership request The secret value is known as a token, and should be some large
random value that you store in anticipation that the user will indeed bring it back to you after checking his mail You can include a link in the email that encodes the token as a GET variable,
so that the user simply has to click that link in order to submit the token back to the verification script This kind of link is sometimes referred to as a one-time URI
The following code implements a simple mailbox verification scheme, and can be found also as mailboxVerification.php in the Chapter 18 folder of the downloadable archive of code
for Pro PHP Security at http://www.apress.com.
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Email Address Verification</title>
</head>
<body>
<?php
// the user wants to submit an email address for verification
if ( empty( $_POST['email'] ) && empty( $_SESSION['token'] ) ) {
?>
<h3>Verify An Email Address</h3>
<form method="post">
<p>Your email address: <input type="text" name="email" size="22" />
<input type="submit" value="verify" />
SnyderSouthwell_5084C18.fm Page 352 Wednesday, July 20, 2005 5:17 AM
Trang 11C H A P T E R 1 8 ■ V E R I F Y I N G Y O U R U S E R S ’ I D E N T I T I E S 353
// continues mailboxVerification.php
// the user has just submitted an email address for verification
elseif ( !empty( $_POST['email'] ) ) {
// sanitize and store user's input email address
$email = safe( $_POST['email'] );
Greetings Please confirm your receipt of this email by
visiting the following URI:
$uri?token=$token
Thank you
EOD;
// build subject and send message
$subject = "Email address verification";
mail( $email, $subject, $message );
// store in session (or new users table)
$_SESSION['email'] = $email;
$_SESSION['token'] = $token;
?>
<h3>Token Sent</h3>
<p>Please check your email for a message marked
"<?= htmlentities( $subject, ENT_QUOTES, 'utf-8' ) ?>"
</p>
<?
}
// mailboxVerification.php continues
In the second part of the script, the user has submitted the form, so you import and sanitize her
email address, and prepare to send a one-time URI to her mailbox You generate a token using
SnyderSouthwell_5084C18.fm Page 353 Wednesday, July 20, 2005 5:17 AM
Trang 12354 C H A P T E R 1 8 ■ V E R I F Y I N G Y O U R U S E R S ’ I D E N T I T I E S
PHP’s uniqid() function, in combination with the rand() function for additional entropy This verification system relies on the token being difficult to guess The message sent to the user’s mailbox consists of a brief instruction and the URI of this script, with the token embedded in the query part Both the email value and the token are stored in the user’s session for later retrieval
// and it matches the stored value
if( $_GET['token'] === $_SESSION['token'] ) {
// the user is verified
?>
<h3>Email Address Verified</h3>
<p>Thank you for submitting verification of the email address
<?= htmlentities( $_SESSION['email'], ENT_QUOTES, 'utf-8' ) ?></p>
<h3>Email Address Not Verified</h3>
<p> the email address you submitted has not been verified
<p>Please check your
<?= htmlentities($_SESSION['email'], ENT_QUOTES, 'utf-8' ) ?>
mailbox and follow the instructions for verifying your email address.</p> <?
}
SnyderSouthwell_5084C18.fm Page 354 Wednesday, July 20, 2005 5:17 AM
Trang 13In the third part of the script, you handle the user’s verification request If she has opened the
email and clicked the one-time URI to submit the verification token, and if that token matches
the token stored in the session, then the email address is considered verified, and (for
demon-stration purposes) an appropriate message is generated If the tokens do not match (which
could happen if an attacker has attempted to spoof an authentic verification), an appropriate
message is displayed, and she is invited to apply again If the user is simply requesting the
script again, but without the token (even though a token has already been stored for this session),
an informative message is displayed in that case as well
When a Working Mailbox Isn’t Enough
Unfortunately, a working email address is no great proof of identity, either At best you have
proven that a communication channel existed at one time, and that someone picked up a
message at that mailbox But the barrier for creating a new email address is extremely low
Anyone who owns a domain has an essentially unlimited number of mailboxes at his disposal,
and anyone who can solve a captcha (discussed in Chapter 17) can obtain a webmail account
at one of the big online email services So possessing a working mailbox doesn’t necessarily
mean that much Over time users will often change their email addresses, either because they
are trying to stay ahead of spammers or because they switch jobs or group affiliations
For a good many applications or services, the working-email barrier to entry may just be
enough If someone has to go through the trouble of creating a new email address in order to
get another account on the system, the thinking goes, he will eventually get tired of doing so
and go away This can hardly be expected if the stakes are high, though; this barrier to entry is
too low to effectively protect a high-profile, publicly available application from abuse
Fortunately, there are alternatives that can be effective
Requiring an Online Payment
Because of the highly sensitive nature of financial transactions, a great deal of care is expended
by banks and funders to protect their customers from fraud, including identity theft By basing
your acceptance of a stranger on her ability to authorize a financial transaction, you raise the
barrier to entry considerably But you also raise the barrier of annoyance for otherwise legitimate
users who don’t have the ability to make online payments
The annoyance factor can be mitigated to some extent by offering sponsorships, so that
low income users, or those unable to pay via online methods, can be given a membership by
a friend or family member Another possibility is to give members in good standing a small
number of invitations that they can hand out to people they know and are willing to vouch for
Verifying a Physical Address
While acquiring a new email address is a simple matter, it is much more difficult to acquire a
new mailing address It may sound overly elaborate, but requiring a verified mailing address is
SnyderSouthwell_5084C18.fm Page 355 Wednesday, July 20, 2005 5:17 AM
Trang 14Using Short Message Service
Short Message Service (SMS) is a protocol used to send electronic text messages to cell phones
If you require new users to provide a cell phone number, you can then send to that number
a text, or SMS, message containing a short token The user receives the token and enters it back into the interface as proof of identity By using SMS, you can thus tie an applicant’s identity to
a cell phone number with presumably valid billing information This technique relies on the fact that cell phone service is relatively difficult and expensive to obtain, but is also fairly common among Internet users A useful tutorial on using PHP with SMS can be found at http://codewalkers.com/tutorials/90/1.html
SMS messages can be sent either by using one of the widely available commercial SMS gateway services (a Google search for “sms gateway” returns over a million records), or by plugging a Global System for Mobile Communications (GSM) modem into your server Of course, if you choose the latter method, you will need to have an account with a cellular provider SMS messages can cost up to 5 cents per message, but bulk plans exist with much lower rates
We discussed this kind of two-factor authentication at some length in Chapter 9
Requiring a Verified Digital Signature
Certificate Authorities are in the business of verifying identity, and a valid digital signature, countersigned by a respected CA, is widely considered just about the best identity verification device you can get, possibly even better than meeting a person face to face
This form of identity verification requires the would-be registrant either to have or to obtain a digital Personal Certificate (not to be confused with the public Server Certificates that
we discussed at length in Chapters 6 and 7) from a recognized CA Such Certificates are increasingly being required in technologically sophisticated organizational settings (like graduate schools
of Computer Science) where use of facilities needs to be highly restricted; such organizations often generate their own certificates for the valid users
Such personal certificates are widely available, but not all of them require identity verification There are, generally speaking, three classes of verification:
1. Class 1 Certificates verify that the applicant has access to a working email account
2. Class 2 Certificates confirm the information provided by the applicant with information
on file at a credit bureau or financial institution
3. Class 3 Certificates require the physical presence of an applicant before the CA, a judge,
or a notary
SnyderSouthwell_5084C18.fm Page 356 Wednesday, July 20, 2005 5:17 AM
Trang 15C H A P T E R 1 8 ■ V E R I F Y I N G Y O U R U S E R S ’ I D E N T I T I E S 357
Obviously a Class 1 Certificate will not prove an identity with any greater validity than requiring the
return of a token delivered to an email address, although, since it does (typically) involve paying
a fee, it may keep out casual abusers by making the creation of multiple identities expensive
If you expect a personal digital signature to provide greater assurance of a registrant’s identity,
you must require a Class 2 or 3 Certificate
Obtaining the digital signature of a potential registrant involves one of two methods:
1. Present her with a secure (HTTPS) page and require that her web browser present an
acceptable Personal Certificate
2. Send her an email message with a valid Reply-to address, and require her to digitally
sign the reply using an acceptable Personal Certificate
By verifying both the signature itself and the CA’s signature on the accompanying Certificate,
you can reliably match the applicant to a real-world identity Different providers include
different information in their various classes of Personal Certificates, so if you are requiring
a higher class, you will have to check closely to ensure that what is presented is of the correct
class
A remote but potential problem with this scenario is that an applicant may be using a
browser or an email client that for some reason is incapable of installing a certificate, thus
disqualifying herself for purely accidental reasons
Summary
Applications that make public user-entered information are subject to abuse by users who
hide behind anonymity or create multiple identities
In this chapter, we have described such abusers and the kinds of abuse they cause Such
abuse can’t be stopped before it begins, but it can be to some considerable extent prevented by
making sure that the identity of each potential user is verified (which allows you to exclude
proven abusers who attempt to re-register) We provided a script for verifying a potential user’s
working email address, and a template for a registration process that involves sending a token
to that working email address with the requirement that it be returned in order to accomplish
the registration We then described briefly several other more complex methods for making
multiple registrations difficult or not worthwhile for an abuser
In Chapter 20, we will take up another issue involved in secure operations, preventing
data loss
SnyderSouthwell_5084C18.fm Page 357 Wednesday, July 20, 2005 5:17 AM
Trang 17Chapter 10 initiated the discussion of system-level access control, discussing user and group
lists maintained by the operating system, and control enforcement through defined file
owner-ship and permissions This chapter continues that topic of access control; here we’ll show you
how to extend it to the users of your online application
It is certainly possible to use a similar method of system accounts to control access to an
application, but we recommend against the practice for three reasons:
1. It is impractical to use file ownership and permissions to control access to files and
scripts that must all be readable by the webserver user _`S`Uj
2. An online application should never be allowed to create (or even expose the existence
of ) system-level user accounts Besides making it difficult to scale an application across multiple servers, each additional system account is a potential agent of system-level access The exposure of valid usernames on the system could also be extremely helpful
to an attacker
3. Most databases have their own systems for access control; any dynamic application
that used system-level accounts for access would logically also need a database account for each user
Application developers must therefore implement within the application their own systems
for enforcing user privileges Different user types or classes typically require different levels of
access to the information stored in an application For instance, administrative users must
be granted abilities that normal users don’t have Furthermore, the level of access can vary
according to location In any moderately complex application, there will be users who must
have general access privileges, but who should not have access to certain sensitive or
inappro-priate locations Or in collaborative applications, groups of users may need to act as teams,
sharing access to various resources
Determining appropriate access rules is tricky enough, but consistently enforcing them
can be even more difficult After all, an application must control not only access to
informa-tion, but also to its own functionality, and it must do so in a manageable way Exposing
administrative rights to the wrong user can be a recipe for disaster, but making it too difficult
SnyderSouthwell_5084.book Page 359 Wednesday, July 27, 2005 12:24 PM
Trang 18we will sketch out a sample authorization object that could be implemented in PHP.
Application Access Control Strategies
In this section, we will discuss many of the possible ways that application developers can limit access within their applications
Because different approaches are suited to different kinds of user bases, we will start with
a simple application and then scale it to different levels of complexity This model of gradual and incremental development is a very common one, and it commonly produces systems with the same kinds of inconsistencies and illogicalities as the one we will show here This model is
most emphatically not the one to follow unless you are willing to scrap it all down the road
when it is no longer able to meet your expanding needs
Eventually, we will get to the right way to set up such a system But to understand why that
is the right way, you need to understand what the other possible strategies are, and why they eventually won’t work, or at least will lead you into dead ends, as the needs of your expanding application demand even more complexity
So let’s begin now, by imagining that you are the tech guy at the hip new online magazine
examplE.Info, for which you have built a nice little Content Management System using PHP
and MySQL
Separate Interfaces
Since examplE.Info consists right now of just three writers plus a part-time photographer,
your application needs only two levels of access: public and private On the public interface, hhhViR^a]VZ_W`, visitors can view articles and photos and leave semi-anonymous comments, but they cannot do anything else Visitor comments are semi-anonymous, because user iden-tification submitted with comments is optional and unverified
On the private interface, the password-protected T^dViR^a]VZ_W`, the members of the staff can write copy, add photos, and copyedit each others’ work They can also preview their own articles and reply to the anonymous users’ comments Anyone who has logged in to the private interface can carry out any of the available actions Even as the staff starts to grow, this is considered safe because your CMS logs the username along with each request, so that responsibility for all changes can be tracked By keeping anonymous users on a completely different site, there is no chance that a disgruntled reader will be able to deface an article or delete comments he doesn’t like
This segregation of interfaces is one fundamental approach to the problem of access control, because it allows you to easily create as many broad classes of users as you need At the
magazine, these classes are staff and users To the extent that there is a collection of command
line scripts to handle moving ready-to-publish content from T^dViR^a]VZ_W` to
hhhViR^a]VZ_W`, there is a third interface and therefore a third class of users, administrators.
Time passes, and there is about to be a fourth The web audience has grown, as has the
number of comments, so the magazine has decided to hire content moderators who need their
SnyderSouthwell_5084.book Page 360 Wednesday, July 27, 2005 12:24 PM
Trang 19C H A P T E R 1 9 ■ U S I N G R O L E S T O A U T H O R I Z E A C T I O N S 361
own interface (they will not be writing and editing copy, so they are not staff; and they will not
be carrying out administrative tasks, so they are not administrators) You go home and create
T`^^V_edViR^a]VZ_W` over the weekend, which allows for the efficient review and detailed
management of all comments Each of the moderators gets an account
A few weeks more pass The two new photographers need an image manipulation
inter-face, where photos can be rotated, cropped, and filtered and then put back into T^d So that’s
another interface you need to build, aY`e`dViR^a]VZ_W`
Now that the magazine is becoming successful, the writers can’t be allowed to publish any
more stories with typos, so you disable the publishing actions in T^dViR^a]VZ_W` and
reim-plement them at VUZeViR^a]VZ_W`, to which only the editors have access.
Your simple CMS has grown from two interfaces to six, with seven distinct classes of users
that you serve: moderators, writers, photographers, editors, administrators, and both registered
and anonymous public users So far, keeping the interfaces separate has worked But we will
eventually need to allow more than one kind of user to use the same interface Separate
inter-faces by themselves can’t handle the newly increased demands of your application
User Types
The readers of examplE.Info are a fiercely loyal bunch, and growing in number You have
already added a more sophisticated membership mechanism (allowing visitors to register, and
furthermore allowing them to become paid subscribers if they wish) and a better comment
engine The principals are considering adding advertising to the public site in order to generate
some needed revenue, but paid members should continue viewing a mostly ad-free version of
the site So in addition to building an ad-manager interface, you are going to need to create two
different types of users in the public interface, so you can show ads to some users but not to others
You have been using interfaces to distinguish among classes of users But user types offer
a second way of managing access in an application, actually providing a finer grained, or more
specific, access control mechanism Everything that you did with separate interfaces early on
you could have done by assigning one or more user types to the various users For instance,
only users of type moderator would be allowed to approve comments, and only users of type
editor would be allowed to publish articles In this way, you would not have needed so many
distinct interfaces To put it another way, if you had known then what you need now, you might
have started differently
But you don’t have time right now to refactor the whole application to take advantage of user
types; you need to get those ads up So you build a banner ad manager at RUdViR^a]VZ_W`, and
tweak the hhh templates to display the ads, with code something like the following:
If a user isn’t logged in as a member, or is logged in but is not a paid subscriber, then your script
adds the JavaScript that inserts a banner ad This means adding a new type column to the
members table in the CMS, and setting the type of paid subscribers accordingly A more flexible
system would abstract this scheme into two tables: one listing all possible valid member types
(which could be expanded as new types of members are brought on board), and another tying
SnyderSouthwell_5084.book Page 361 Wednesday, July 27, 2005 12:24 PM
Trang 20362 C H A P T E R 1 9 ■ U S I N G R O L E S T O A U T H O R I Z E A C T I O N S
particular members to one or more types of membership That way a given member could be
of type sports and type paid at the same time.
Whichever of these two methods of implementing user types you choose, the public hhhViR^a]VZ_W` interface is now home to two broad classes of users, anonymous and members, and the members class contains many different user types
Within the code for the hhh interface in general, you have created logic that displays
different specific interfaces, and allows or denies various actions, based on the type of the
member making the request Actions are the discreet bits of functionality that exist within an interface (in larger applications they are often factored out into separate scripts that are included
at run-time based on the request) For instance, nonmembers can carry out the register action
if they choose to, but (until they have registered) they are denied access to the login and change
password and set preferences actions which are available to members Both members and
nonmembers are allowed to use the post comment action.
But actions aren’t always so clear-cut As we explained previously, paid members can view
articles without ads, whereas everyone else sees the regular view We might say that paid
members can carry out the skip ads action Even though the skip ads logic may be bundled into the same script that carries out the view action, it is a good idea to treat it conceptually as a
separate action that is allowed only for charter members
User Groups
The once-humble online magazine continues to grow, and one day the CEO drops by with
great news: examplE.Info has signed a deal with rival online magazine WebZine, and will be
publishing their magazine using your CMS, with an eye toward possibly sharing content in the future Outwardly, you are smiling, but inwardly you wonder, “Things are getting really complicated; how am I going to keep all of the actions appropriately separated within the
system?” The examplE.Info staff is not going to want to allow the WebZine staff access to their
unpublished articles and photos!
One possible choice would be to build a full complement of separate interfaces (hhh, T^d,
aY`e`, VUZe, and RUd) for exclusive use by the WebZine staff This choice could potentially result
in two separate codebases, accessing to two separate databases and used by two separate collections of users That would be a maintenance nightmare
The easiest and quickest way for you to accomplish this task, then, would be to assign users
to groups, in a model similar to that of a unix filesystem (which we discussed in Chapter 10) Users belonging to the example.info group are given access to articles and photos produced by other users in their own group, and users belonging to the WebZine group are similarly given access
to their own articles and photos In this way, all users share the same codebase, but access to
content (that is to say, access to different locations within the system) is controlled based on a
user’s group identity
In an online application, particularly a web application, location is synonymous with URI
(and with URL; indeed, location is the L in Uniform Resource Locator) Since each article or
photo in T^dViR^a]VZ_W` has a distinct URI that points to it, we can say that each article or photo resides at a particular virtual location By assigning users to various groups, and assigning locations within the application to those groups, you can control which users have access rights to each article and photo
Implementing this method for additional access control across all of the interfaces at ViR^a]VZ_W` has one very attractive feature: it does not (yet, as you will see) require complete
SnyderSouthwell_5084.book Page 362 Wednesday, July 27, 2005 12:24 PM
Trang 21C H A P T E R 1 9 ■ U S I N G R O L E S T O A U T H O R I Z E A C T I O N S 363
refactoring But you will need to add two tables to the database: one to keep track of groups,
and another to track user-to-group assignments You will also need to add a groupid field to the
table or tables that keep track of articles and photos, or create a separate table that assigns
locations to groups Still, this seems like a manageable task for right now
This strategy will be even more efficient if the articles and photos are saved in some sort of
hierarchical namespace, as they typically are in a traditional static website In this case, you
would simply assign articles and photos found in, for example, Yeea+ T^dViR^a]VZ_W`
ViR^a]V to the example.info group Then you can associate other locations with other groups,
and the problem is solved
On the other hand, if the articles and photos are identified only by an ID, as in the URI
Yeea+ T^dViR^a]VZ_W` RceZT]VdaYa0ZU.$'! (which makes storing and referring to them
easier), then you will need yet another field to associate an item ID with a group affiliation
Once groups are implemented, you have a lot to juggle, but you can fairly easily allow or
deny actions on specific locations within your application depending on user class (the
inter-face being used), user type (the privileges or appropriate actions allowed), and group ID The
members of the user class editors, who are using the publishing interface at VUZeViR^a]VZ_W`,
are all generally allowed to carry out publish, feature, and archive actions on the articles and
photos in the system But with the advent of groups, logic must be written into the VUZe
inter-face that prevents editors belonging to one group from calling those actions on articles or
photos belonging to other groups
Adding Content Sharing
Your group ID system has worked so well that the boss is now ready to move ahead with content
sharing, which complicates things considerably The staff of WebZine might create and publish
an article that later is assigned also to the example.info group, thus fulfilling the CEO’s goal of
sharing content But what happens if members of both groups need to collaborate on that or a
new article? You could create a third group made up of those users who need to work together,
which could be a perfectly acceptable workaround in the current system But as the examplE.Info
media empire acquires new properties and branches out into new media, the number of groups
required will increase, and new group-checking logic will need to be implemented in every
interface to the system
Your original application has grown incredibly complex, and the userbase has diversified
to the point where very specific access policies need to be enforced to keep people from
acci-dentally or maliciously carrying out actions that they are not authorized to carry out Your logic
for preventing unauthorized access needs to be implemented against group and/or user type
across many different interfaces Unfortunately, due to the organic development of the system,
the authorization logic is, as we have seen, completely ad hoc In the public interface, it is
based on user type In the management interfaces, it is based on group And there are still more
specialized interfaces, such as T`^^V_ed and aY`e`, that have their own authorization schemes
It is becoming increasingly difficult to say which users are allowed to carry out specific actions
on a given article or photo, because that information is spread out over at least three different
subsystems
With plans in the works to expand examplE.Info’s offerings to include video content as
well, you are faced with building yet another complex interface that has its own set of user
types and groups It is time, in fact well past time, to consider a centralized system for granting
or denying access according to complex policies
SnyderSouthwell_5084.book Page 363 Wednesday, July 27, 2005 12:24 PM
Trang 22364 C H A P T E R 1 9 ■ U S I N G R O L E S T O A U T H O R I Z E A C T I O N S
Roles-based Access Control
One such system, and the one we recommend, is known as Roles-based Access Control (RBAC)
In the RBAC model, users are assigned roles, like the roles in a play, that apply to various locations
or interfaces within the system A user might be an editor in one location, but just a humble
writer in another A community member might be granted the moderator role in areas where
she possesses particular expertise An advertising account manager might be granted the
adManager role across one or more of the websites being served by the system.
Each role carries with it a well-defined set of permissions, that is, a list of those actions that
are allowed to all the users possessing the same role at the same location Writers are allowed
to create and view unpublished articles within the CMS Video producers are allowed to add video content to any article Article owners are allowed to edit their articles and add photos
Those examples are the general case There may be specific locations within the system where
users possessing the writer role are allowed to create video clips and add video to their articles There may also be locations where writers are not allowed to add photos on their own We
illustrate this concept in Figure 19-1, which shows one example of the interrelationship of role assignments, location, and permissions for staff member bfranklin
Figure 19-1 An illustration of the effect of role assignments on permissions at a location
Staff member bfranklin has three different role assignments, each applying to a different
location at examplE.Info At WVRefcVd, he has the role of editor, which gives him permission to add, view, or edit items Therefore, at the specific location WVRefcVd #!!& !% XRUXVedYe^] (which corresponds in this hierarchical filesystem to a specific article) bfranklin may request the add, view, or edit actions, but not the delete or publish actions
SnyderSouthwell_5084.book Page 364 Wednesday, July 27, 2005 12:24 PM
Trang 23C H A P T E R 1 9 ■ U S I N G R O L E S T O A U T H O R I Z E A C T I O N S 365
Focusing on roles doesn’t mean that we throw group affiliation out the window, because
groups can collect various roles and/or locations into convenient, easy-to-reference units The
ultimate difference between a group and a role is that a group is a collection of users, while a
role is a collection of allowed actions The articles, photos, and videos (that is, the locations)
within a system can have group affliations, as can the roles for working with them Blanket
permissions can then be assigned by group, and special roles can be created to modify those
blanket permissions for particular users or locations
A well-implemented roles-based system can account for all of these special cases, because
it can assign different sets of permissions to the same role name at different locations, just as it
assigns the same role to different users or groups at different locations This model, then, allows
for extremely fine-grained access control, and allows that control to be managed from a central
interface in your system We will spend the rest of this chapter exploring the implementation
of a suitable scheme for authorizing specific actions based on a user’s role
EXISTING ROLES-BASED ACCESS SYSTEMS
RBACs have been implemented in many of the languages commonly used for Internet-based applications
The Java Authentication and Authorization Service (JAAS; see Yeea+ [RgRdf_T`^ ac`UfTed
[RRd for more information) is now integrated into the Java 2 Software Development Kit
Zope (written in Python; see Yeea+ hhhk`aV`cX for more information about the Open Source
version, and Yeea+ hhhk`aVT`^ for information about the commercial version) is a comprehensive
content management platform with built-in support for RBAC
The Lightweight Directory Access Protocol (LDAP; see Yeea+ V_hZ\ZaVUZR`cX hZ\Z =URa and
RFC 1777 at Yeea+ cWT_Ve cWT"(((Ye^] for more information) was originally developed at the
University of Michigan as a simple desktop- and Internet-accessible version of the heavyweight X500 enterprise
directory system (see Yeea+ V_hZ\ZaVUZR`cX hZ\Z I&!! for more information) LDAP was originally
intended to facilitate lookup of email addresses, but is actually capable of much more general searching Since
it is already supported by a wide variety of vendors, and an Open Source LDAP server is available from Yeea+
hhh`aV_]URa`cX , it could be a good solution for creating an RBAC system You sacrifice some control,
perhaps, by putting everything in the hands of an LDAP server, but you gain a world of tools for administering
the database of users, groups, roles, and permissions However, although skipping straight to LDAP does save
you from having to build the database and the roles-administrator interface yourself, the complexity of using
it is, we believe, justified only if LDAP is going to be used with other applications that already speak it
Authorization Based on Roles
The role is the fundamental unit of a Roles-based Access Control system A role is a collection
of three pieces of information:
1. A dynamic reference to a location, ideally in an object tree A role could also point to a
request URI, a parent directory, or almost anything else that is unambiguously specifiable
2. A collection of permissions; in other words, a list of the actions that can be carried out by
a user who has been assigned the role
SnyderSouthwell_5084.book Page 365 Wednesday, July 27, 2005 12:24 PM
Trang 24366 C H A P T E R 1 9 ■ U S I N G R O L E S T O A U T H O R I Z E A C T I O N S
3. A collection of role-to-user assignments A role assignment is like a badge worn by a
user, giving him the authority to do particular things
We illustrate this concept in Table 19-1, which shows three sample roles
In these examples of role-based authorization, the “sports-producer” badge with ID 12345 grants permission to add, delete, and edit at the gZUV`ViR^a]VZ_W` da`ced location The badge with ID 34567 limits the sports-producer’s permissions in the da`ced W``eSR]] dfaVcS`h] location Whereas a user with a sports-producer badge can normally add, edit, or delete, in this particular section she is limited to edit permission only The “editor-in-chief” badge with ID 3 grants wide-ranging permissions to whoever bears it
We illustrate the awarding of badges to specific users in Table 19-2
Here you simply assign an already identified badge name to a user or group of users When
a user visits a location, two pieces of information are looked up to define that user’s possible actions:
1. What roles does the user possess, by role name, at this location?
2. What are the permissions allowed for those role names at this location?
These RBAC lookups enable an application to discover the full set of actions allowed for a given user at a particular location, and to grant or deny execution of actions accordingly
In this tripartite nature, a role is almost exactly comparable to a MySQL 8C2?E statement:8C2?E:?D6CEFA52E656=6E6@?ViR^a]VUSE@R^Z1]`TR]Y`de,
Table 19-1 Sample Roles for examplE.Info
gZUV`ViR^a]VZ_W` da`ced add, delete, edit sports-producer 12345
editor-in-chief Jose, Ettore
SnyderSouthwell_5084.book Page 366 Wednesday, July 27, 2005 12:24 PM
Trang 25C H A P T E R 1 9 ■ U S I N G R O L E S T O A U T H O R I Z E A C T I O N S 367
That statement grants permissions (to insert, update, and delete) on locations (anywhere
at ViR^a]VUS) to a user (R^Z, so long as she connects via localhost) Roles-based
authentica-tion differs from this only in that it assigns permissions on locaauthentica-tions not to an individual user
(or group of users) but rather to a unique identifier, or badge; it then allows you to assign that
badge to one or more users This system is fine-grained enough to allow each user to have
different permissions at every unique URI in your application, if that is what you need
What Roles Look Like
While a role may be stored in a database or on an LDAP server, its best expression for our
purposes as developers is as a PHP object Here are some samples of what such a role object
could look like in a project like examplE.Info:
This set of rules means that any user assigned the badge of webzine member (with an ID of 55)
is authorized to view objects and add comments (but not to refresh the dynamic index of articles)
at Yeea+ hVSkZ_VT`^ >V^SVcd@_]j and anywhere below that, such as >V^SVcd@_]j RceZT]Vd
A9APDVTfcZejP2_UPJ`faYa
If your application uses a hierarchical structure, such as a filesystem or a persistent object
database (a technique for storing application objects in a hierarchical database between requests;
see Yeea+ V_hZ\ZaVUZR`cX hZ\Z @S[VTecV]ReZ`_R]P^RaaZ_X and Yeea+ hhhR^Sjd`WeT`^
aVcdZdeV_TV=RjVcYe^] for more information), permissions can be inherited by child locations
within the hierarchy, much as YeRTTVdd settings are inherited by directories in a traditional
website Because permissions can be explicitly allowed or denied to a given badge at a given
location, you can fence off areas within the hierarchy where you don’t want to allow actions
that would otherwise be inherited, something like this:
Notice how no actions are explicitly allowed for role 56 The view action is inherited by the
webzine member badge from the actions allowed at HVSkZ_V >V^SVcd@_]j, and so doesn’t need
to be specified again The only reason role 56 exists is to deny to anyone possessing the webzine
member badge the addComment action on articles and photos that have been moved into the
archives Because role IDs 55 and 56 have the same badge name, they represent the same
general role but with different permission sets at different locations within the system
The Name of the Role
Many companies, and most conventions, use ID badges to control access areas and services At
concerts, if you don’t have a backstage pass, you can’t hang out with the roadies or feast on the
SnyderSouthwell_5084.book Page 367 Wednesday, July 27, 2005 12:24 PM
Trang 26368 C H A P T E R 1 9 ■ U S I N G R O L E S T O A U T H O R I Z E A C T I O N S
catering The virtual badges used in Roles-based Access Control work the same way Possession of
a role confers any number of permissions on the bearer, depending on where that person is in the system
The role names, the location names, and the names of the actions that you allow and deny
in a role are all completely arbitrary and application dependent, but they should of course be sensibly descriptive as well as consistent within an application An example taxonomy, designed
again for the needs of our theoretical examplE.Info, follows (The names of the roles are shown
in boldface simply to make the different roles more easily distinguishable.)
Editor
The global editor role in our CMS is the most powerful single role; it grants the bearer nearly
unlimited permission in the areas where it is valid A top-level editor can add, view, and edit any article or photo, can update metadata and indexes, and can feature, archive, or delete any
of the objects in the system
The standard member role, by contrast, is perhaps the most restricted, allowing nothing but
the viewing of public materials, and adding comments to them:
c`]V/_R^V.PHPEHU,
c`]V/]`TReZ`_. ,
c`]V/R]]`h.RccRjgZVhAfS]ZTRUU4`^^V_e,
Anonymous
It’s a good idea to give even unauthenticated users an explicit role name, even if the only thing
it allows is the login action In this example, we also allow a user with the anonymous role to
view (but not add comments to) public articles and photos
c`]V/_R^V.DQRQ\PRXV,
c`]V/]`TReZ`_. ,
c`]V/R]]`h.RccRj]`XZ_gZVhAfS]ZT,
Author and Photographer
The content-creation roles authors (for articles) and photographers (for photos) don’t possess
the ability to work with metadata or publishing features as the editor does, but otherwise have
a fairly complete set of permissions
SnyderSouthwell_5084.book Page 368 Wednesday, July 27, 2005 12:24 PM