Well-Intentioned Destruction

« Return to Article
  • anonym 2009-04-07 11:02
    this comment will auto destruct
  • Andy Goth 2009-04-07 11:09
    AOL in 1996: "Data Retrieval Failure."

    Sounds about right. :^)
  • Captain Oblivious 2009-04-07 11:10
    If you rely on a form button to delete content, also remember to use the POST method, not GET!
  • Jim 2009-04-07 11:16
    if(!$Auth) {
    header('Location: index.php');
    exit;
    }

  • Mark Bowytz 2009-04-07 11:22
    Until the Wayback Machine fixes itself, check out aol.com from 1997 here and read all about the beta release of some crazy piece of software called "AOL Instant Messenger"
  • Havstein 2009-04-07 11:22
    Yeah, that will teach them to not make their site RESTful!
  • Edgeman 2009-04-07 11:25
    And that's why header redirects like this need a die(); afterward... even if you never expect the code to hit it, better to be safe than sorry.
  • PerfectlyNormal 2009-04-07 11:27
    Havstein:
    Yeah, that will teach them to not make their site RESTful!


    Doesn't sound all that RESTful to me. If it was, they'd have used DELETE to delete pages. And I *really* doubt the Wayback Machine's bot sends off DELETE-requests to pages. It should only do GET.

    So if they had made it RESTful, this wouldn't have happened.

    A good idea is to never let a GET-request do anything that changes things.

    "Some methods (for example, HEAD, GET, OPTIONS and TRACE) are defined as safe, which means they are intended only for information retrieval and should not change the state of the server. In other words, they should not have side effects, beyond relatively harmless effects such as logging, caching, the serving of banner advertisements or incrementing a web counter. Making arbitrary GET requests without regard to the context of the application's state should therefore be considered safe."
    http://en.wikipedia.org/wiki/HTTP#Safe_methods
  • brazzy 2009-04-07 11:38
    I don't believe you can call yourself a web developer until you've built an app that uses hyperlinks for deletion and have all your data deleted by a search bot.

    I managed to do that on a system not even connected to the internet - it was an experimental app for my own use. And then I decided to experiment with an indexing engine as well...
  • MrEricSir 2009-04-07 11:38
    If this site doesn't even validate credentials to DELETE a page, what about adding or modifying content?

    This sounds like every website defacer's wet dream.
  • Madball 2009-04-07 11:42
    This brings back memories. Had almost the same exact thing happen several years ago. We had a a backdoor admin page for a supplier "portal" for creating announcements. It had convenient little edit/delete links next to each pre-existing posting. We even had some nice internal documentation which spelled out where and how to create announcements. All was well with the world for a couple years (security was not really a concern).

    Until, the infrastructure guys bought a google search appliance. It dutifully found the documentation -> backdoor -> followed each delete link. It took some effort to track it down. After some chuckling, we started blaming google for all future mysterious occurences.
  • Patryk Zawadzki 2009-04-07 11:43
    Actually it has nothing to do with the header. Some headers are not supposed to be followed by a page body and Apache will actually go to great lengths to remove the body if it encounters such a reply before sending it to client. Therefore the bot can ignore all the headers it wants.

    The actual WTF is sending a header but *not aborting the control flow* so the header gets cached for output while PHP is still happily executing the rest of the file (presumably deleting stuff).

    So I say there are two WTFs: the actual one pointed above and the explanation at the end of the original story.
  • Foo 2009-04-07 11:43
    Exactly.
  • ckoppelman 2009-04-07 11:45
    or just add rel="nofollow"
  • evilspoons 2009-04-07 11:49
    brazzy:
    ...I managed to do that on a system not even connected to the internet - it was an experimental app for my own use. And then I decided to experiment with an indexing engine as well...


    You, sir, win the internet today.
  • redwall_hp 2009-04-07 11:49
    *Sigh*

    This is why you put an exit; statement after the header call...

    If you add that one line, you wouldn't have the problem. The script would just stop running after the header is sent. (Some servers won't execute redirects properly unless you do this anyway...)
  • tulcod 2009-04-07 11:58
    Patryk Zawadzki:
    Actually it has nothing to do with the header. Some headers are not supposed to be followed by a page body and Apache will actually go to great lengths to remove the body if it encounters such a reply before sending it to client. Therefore the bot can ignore all the headers it wants.

    The actual WTF is sending a header but *not aborting the control flow* so the header gets cached for output while PHP is still happily executing the rest of the file (presumably deleting stuff).

    So I say there are two WTFs: the actual one pointed above and the explanation at the end of the original story.

    Exactly, even if browsers would honor the Location header, the full page gets executed, no matter if the browser already started requesting another, so this is not the real leak.

    So indeed, TRWTF is that even after so much trouble, Dan still doesn't understand the issue.
  • jonnyq 2009-04-07 12:00
    The article is a bit misleading about headers and PHP, but not off by much.


    $authorized = authorize(); // let's assume this is false
    if(!$authorized) {
    // this, by default, sets a 302 status header
    header("Location: index.php");
    // because I'm stupid, this was omitted
    // die();
    }

    // this works. PHP happily continues after the header() call
    deleteSomething();

    // this fails. Since the 302 status header is set, PHP is going to die() before sending any actual output.
    echo "Hello World!";

    // this fails, too. PHP done died.
    deleteSomethingElse();


    So, setting a Location header on a failed auth is OK. Just make sure you die() before doing anything else.

    Most robots/spiders handle 30x redirects just fine. It's just that PHP doesn't die() immediately until you try to output something after that header was sent.

    Yes, using a <form> would keep the spider away, but that won't keep wrongdoers at bay. Yes, deletes should be done with a POST and not a GET, but that's less important than just making sure the damned script dies when auth fails.
  • wrong! 2009-04-07 12:00
    nofollow is a hint to search engines that you do not endorse the contents of the link because it may have been added by users. It's very badly named: it does not mean "do not follow this link", it means "I do not vouch for the value of this page".
  • Kuroki Kaze 2009-04-07 12:11
    Man, that was an Epic Fail :) Not that i prone to that ones myself, but it was hell of a funny :)
  • Neil 2009-04-07 12:21
    Wow, 20 comments about a PHP blunder and we've yet to see someone say the usual mantra, "TRWTF is PHP".
  • Ryan 2009-04-07 12:28
    Or.. stop using if (!auth) and instead put if(auth) at each page.

    A crawler won't auth, so they get nothing.

  • chris 2009-04-07 12:32
    The WTF is that the check NEVER works unless you add exit or die after the location header. It doesn't matter whether the Location header is interperted by the client - after sending the Header, the script will still happily continue to actually delete the page - so EVEN IF the client interprets the Location header, the page will still be gone. So the problem is not exactly that the programmier chose do use an idemponent method (GET) for a non-idemponent action (delete a page) - that is in itself not a security issue. It's bad design, agreed, but unproblematic in a security sense. The problem is that the programmer either simply forgot the "exit" (which is a stupid but very human mistake, could happen to anyone) or the programmer had no clue what in client/server communications actually happens. To repeat myself: The Location header is not the problem (well, actually it's wrong since the HTTP spec requires absolute URIs but let's ignore that for the moment) - the problem is what happens after sending the header. The script should terminate instead of continue.

    The other WTF here is that the article author also draws the wrong conclusion... Also, using a form and POST by itself will only solve the problems of crawlers - NOT the security issue when trying to protect against motivated attackers. FAIL I dare say. ;-)

    By the way: Another WTF is that - apparently - there's no CSRF protection in that script...
  • rohypnol 2009-04-07 12:34
    ckoppelman:
    or just add rel="nofollow"

    Yeah, that'll teach those perky script kiddies to stay away from your website!
  • Palad1 2009-04-07 12:36
    If only there was a way to execute a different piece of code when the if condition failed!

  • Eevee 2009-04-07 12:42
    I'm a little disturbed that several people now have condoned wrapping almost an entire file's worth of code in an if() block. Flow control is your friend.
  • Satanicpuppy 2009-04-07 13:05
    Eevee:
    I'm a little disturbed that several people now have condoned wrapping almost an entire file's worth of code in an if() block. Flow control is your friend.


    If you're doing authentication on a per-page basis, what's the alternative? HTML isn't stateful, you can't control when people are going to hit which pages, so you can't really maintain flow. If you could, all you'd have to do is authenticate them on the first page, and then you could be sure that every page thereafter was fine.

    Mind you, I hate crap like the example. If you do "If($auth){...} you're going to get screwed by someone who understands cookies. You need to run an actual sanity check on the cookie data.
  • Voodoo Coder 2009-04-07 13:07
    Neil:
    Wow, 20 comments about a PHP blunder and we've yet to see someone say the usual mantra, "TRWTF is PHP".


    I think everyone is off today...I always like to visit these articles about an hour after I see them in my RSS feed, then I can try to bet myself on what will be getting nitpicked and torn apart in the comments...I find that I'm right 9 out of 10 times.

    Today though...I gave myself 10 to 1 that "differentiate the difference between..." would be the major topic of discussion, with camps from one side defending its usage and the other, saying it is the most epic failure on the interwebz today.


    And I lost...you cut me deep, TDWTF...you cut me real deep.
  • RHuckster 2009-04-07 13:11
    Look on the bright side! You can at least recover your data through archive.org...
  • wee 2009-04-07 13:32
    This happen at [a large search company with a funny name] a few years ago when I was there. Some guys was furious, screaming about lawsuits, cold-calling every corporate-looking phone number he could come up with, etc.

    He claimed that we were hacking his site to the benefit of his competitors because he wasn't buying enough keywords. Of course, that's completely ridiculous. I think at one point he restored his site from the pages in the cache even.

    It happened a few times. Finally someone took a look and explained to him that [funny-name]bot was there to index his site, not delete things. So having a link right there in the footer of every page that would let *anyone* delete the page was probably a bad idea.

    I don't recall the outcome, but I think he finally went the robots.txt route to prevent his site from being crawled --by us. Odd that he'd trust his nephew the neophyte coder over people who are largely regarded as experts in their field and more than willing to help him out, but for some people, paranoia is a motivating force.
  • Otto 2009-04-07 13:47
    Let this be a lesson to all: Validating credentials before actually making any changes is better than only validating credentials at the entry point. Better to validate multiple times, if necessary.

    Guy I knew once called me out for doing "unnecessary" authentication checks in every function that, well, required authentication. His reasoning was that the function couldn't be reached unless you were authenticated already. While that was true, at the time, it was an MVC application, and somebody could easily create a view that didn't check auth credentials. Authentication and verification goes at the lowest possible level, not the highest.

    Anyway, it came back to bite him later, when somebody did just that, and since he was the one who pulled my authentication out of the source repository, he was the one who lost his job over it.

    Authentication: It's good job security.

    Also, while POST is your friend for state-changing events, don't rely on it as a safety mechanism. Search engines and other bots can do POSTs too, you know.
  • diaphanein 2009-04-07 13:49
    brazzy:
    I don't believe you can call yourself a web developer when you've built an app that uses hyperlinks for deletion and have all your data deleted by a search bot.

    FTFY
  • Dominic Pettifer 2009-04-07 13:54
    WTF No. 1: Using GETs to do deletes instead of POST.

    WTF No. 2: Not properly authenticating the user in the server-side code that performs the delete/update.

    WTF No. 3: Hiring numpties (who couldn't web-develop their way out of a paper bag) to build you a website.
  • Nathaniel 2009-04-07 14:20
    While using POST is a lot better, and yes, you do want to make sure that the script exits after sending a location header, neither of these alone can prevent an XSRF attack. Don't think your work is done if that's all you've done to secure your web apps.

    http://en.wikipedia.org/wiki/Cross-site_request_forgery
  • Vlad Patryshev 2009-04-07 14:27
    I can't believe it; it is 2009, and it is still happening. Probably the whole world knows about this.

    Would be nice to track the developer that added delete command to GET request. Track those who hire such developers. Track those who hire those who hire such developers. (To make sure we never never never get into such an environment.)
  • Harold 2009-04-07 14:32
    if(!isset($_SESSION['usr_id']) || !isset($_SESSION['usr_name']))
    {
    header('Location: index.php');
    exit()
    }

    would have been the quick fix.
  • Pim 2009-04-07 15:11
    Vlad Patryshev:
    Would be nice to track the developer that added delete command to GET request. Track those who hire such developers. Track those who hire those who hire such developers. (To make sure we never never never get into such an environment.)

    Pfff, the security of POST is grossly overestimated. I know several pieces of web software that carefully check and validate everything sent with GET, but that blindly accept POSTed values as if nothing ever can be wrong with those. Hacking those sites is the easiest thing there is.
  • pink_fairy 2009-04-07 15:13
    Vlad Patryshev:
    I can't believe it; it is 2009, and it is still happening. Probably the whole world knows about this.

    Would be nice to track the developer that added delete command to GET request. Track those who hire such developers. Track those who hire those who hire such developers. (To make sure we never never never get into such an environment.)
    It'll be interesting to see how many more people fire the silver bullet of "POST," or even "DELETE," at this thread. (More interesting than the umpteenth numpty who fails to read the thread first and adds "exit()" to the code, anyway.)
    Otto:
    Let this be a lesson to all: Validating credentials before actually making any changes is better than only validating credentials at the entry point <snip/>... Authentication and verification goes at the lowest possible level, not the highest.

    Also, while POST is your friend for state-changing events, don't rely on it as a safety mechanism. Search engines and other bots can do POSTs too, you know.
    Indeed -- a comment that should be blued, at least for educational purposes.

    While PHP certainly isn't TRWTF here, and I can already hear people muttering "you can write ... in any language," there are certain language communities that encourage programmers to ignore the fact that they are doing two entirely separate things (three, if you count the C in MVC): working on the client side and working on the server side. Maybe it's just the teaching of PHP that's at fault.

    On the server side you can do far, far more horrible things than just allow systematic bot trawling to delete individual records or files. Anything to do with financial transactions comes to mind. Anything like "rm -rf /" would be a bag of fun.

    Web applications should never do anything significant on the server side without proper authentication. (And yes, I've bitten myself on the bum with this one, too.)
  • Jay 2009-04-07 15:16
    There's something fundamentally wrong with any security scheme that relies on the CLIENT for enforcement. Like, umm, what if the user is deliberately trying to vandalize your site? Even if off-the-shelf browsers will respect your security, surely there are plenty of hackers out there smart enough to write their own socket programs.

    This is like those stores that put up "No weapons allowed" signs with the big red slash through a picture of a handgun. Like, yeah, some maniac is going to load up with guns and bombs and head out to the mall with the intention of killing as many people as he can before the police finally arrive to stop him, he's prepared to sacrifice his own life for the pleasure or glory or whatever he gets out of killing innocent people ... and then he's going to see a "No weapsons allowed" sign on the door and say, "Oh, darn, I guess I can't do that here."

    I'm not naive or arrogant enough to suppose that any security measures I build into a system are absolutely 100% secure. A determined, resourceful hacker could probably figure out a way to beat any security I've ever built. But I at least try to give him a run for his money. I want to make him go to more work than, say, having to "view source" on the page to see the password, or having to put a quote mark in his input string to break my SQL.
  • Top Cod3r 2009-04-07 15:23
    Captain Oblivious:
    If you rely on a form button to delete content, also remember to use the POST method, not GET!


    Exactly. TRWTF is that the article writer didn't say this.

    Also, this isn't a problem of obeying headers.

    If I send form data to "controlPage.php?action=delete&page=112", and the page responds with a redirect header but STILL PERFORMS THE ACTION, that is just a matter of the security being ALL FUCKED UP.
  • Dan 2009-04-07 15:39
    Hmmm, so Alexa joins the tiger team...
  • Franz Kafka 2009-04-07 15:57
    Jay:
    There's something fundamentally wrong with any security scheme that relies on the CLIENT for enforcement. Like, umm, what if the user is deliberately trying to vandalize your site? Even if off-the-shelf browsers will respect your security, surely there are plenty of hackers out there smart enough to write their own socket programs.


    You could probably just write a simple FF plugin - ignoring Location: headers and spidering all the delete links on a site sounds like a simple task.
  • Concerned...and shocked 2009-04-07 16:19
    I wonder how on earth the system deleted pages without enclosing the delete-performing block with a check if the user (depending on the user ID, store it any way you like it) is allowed to do so. Overtrustingly damaging.... sheesh
  • Jim 2009-04-07 16:22
    Screw whether it's a Get or Post request.

    Just make sure any page that is normally accessed by authorization actually checks that the user is logged in and authorized to perform that function.

    Otherwise, any user could just type in the link.

    Yes, POSTS could be hacked too. Plenty of code around to submit post data to a page as well. This is why the post target needs to check.
  • GrandmasterB 2009-04-07 16:58
    Jim:
    Screw whether it's a Get or Post request.

    Just make sure any page that is normally accessed by authorization actually checks that the user is logged in and authorized to perform that function.


    You just beat me to this. If the fact that its a GET vs a POST matters to your app, you're doing something very wrong. Properly check the user credentials before doing the deletion, and you will not have this problem, period.



  • nico 2009-04-07 17:10
    Hmmmmm... so why not, first of all, showing the delete link ONLY if you are actually allowed to delete stuff?
    Why would you show a delete link to a spider? It does not really need to index delete pages anyway, so you should prevent it to go there completely with something as simple as:

    if ($canDelete)
    echo '<a href="deleteAllMyWebsite.php">Click me!</a>';
  • rfsmit 2009-04-07 17:10
    brazzy:
    I don't believe you can call yourself a web developer until you've built an app that uses hyperlinks for deletion and have all your data deleted by a search bot.

    That explains a lot. Just don't tell the Web developers who know how to capitalize properly.
  • susciphere 2009-04-07 17:40
    Jay:
    This is like those stores that put up "No weapons allowed" signs with the big red slash through a picture of a handgun. Like, yeah, some maniac is going to load up with guns and bombs and head out to the mall with the intention of killing as many people as he can before the police finally arrive to stop him, he's prepared to sacrifice his own life for the pleasure or glory or whatever he gets out of killing innocent people ... and then he's going to see a "No weapsons allowed" sign on the door and say, "Oh, darn, I guess I can't do that here."

    That would make a nice xkcd strip.
  • 8D 2009-04-07 17:53
    you have single-handedly saved these horrendous comments. thank you
  • Franz Kafka 2009-04-07 17:59
    nico:
    Hmmmmm... so why not, first of all, showing the delete link ONLY if you are actually allowed to delete stuff?
    Why would you show a delete link to a spider? It does not really need to index delete pages anyway, so you should prevent it to go there completely with something as simple as:

    if ($canDelete)
    echo '<a href="deleteAllMyWebsite.php">Click me!</a>';


    Not all spiders declare themselves, and anyway, how would you tell them apart? Best thing to do is actually protect delete links.
  • nico 2009-04-07 18:10
    Franz Kafka:
    Not all spiders declare themselves, and anyway, how would you tell them apart? Best thing to do is actually protect delete links.


    Well... that's what I was saying. The spider will not see the link at all. I guess you need to be a registered user, probably with high privileges, in order to delete stuff. Definitely a spider does not fall in this category, whether it declares itself or not... so just don't show it the link (as you don't show it to guest users or to normal users that can't delete stuff)
  • My Name * 2009-04-07 18:42
    GrandmasterB:
    Jim:
    Screw whether it's a Get or Post request.

    Just make sure any page that is normally accessed by authorization actually checks that the user is logged in and authorized to perform that function.


    You just beat me to this. If the fact that its a GET vs a POST matters to your app, you're doing something very wrong. Properly check the user credentials before doing the deletion, and you will not have this problem, period.





    It's still a good idea to use POST requests for this. I have used links to delete content once and got promptly bitten by an authorized user who used a web mirror tool on that site. Done right the data will at least survive well behaved tools in the hands of your average user.
  • VxJasonxV 2009-04-07 18:54
    FWIW, I always use Location, followed by exit; and/or die();
  • 50% Opacity 2009-04-07 20:16
    I think somebody put something in my morning coffee, I'm starting to things twice, thrice, nay... I could swear I saw the die()/POST/header()/if($auth) comments at least a hundred million times in the last five minutes.

    I'm going back to bed...
  • 50% Opacity 2009-04-07 20:18
    50% Opacity:
    I think somebody put something in my morning coffee, I'm starting to see things twice, thrice, nay... I could swear I saw the die()/POST/header()/if($auth) comments at least a hundred million times in the last five minutes.

    I'm going back to bed...


    Yeah, something's definitely wrong with this coffee...
  • Fnord Prefect 2009-04-07 22:26
    RHuckster:
    Look on the bright side! You can at least recover your data through archive.org...

    I've had to do that a couple of times before - mostly due to users deleting things and not having any sort of backups.
  • Gretta 2009-04-07 22:47
    input type="button"

    No, no, no, no. no!!! It is:

    input type="submit"

    Whoever came up with the "button" that doesn't do anything unless I give the entire internet permission to totally fsck my browser (also called enabling scripts) should be shot. In the leg, so as to live long enough for the torture.

    The form submit capability is there for a reason. Use it!

    And oh yeah, GETs should be idempotent. If you didn't know that already, kindly remove your stinking excrement from my web.
  • Danny Fullerton 2009-04-07 22:47
    Using a POST (input button) instead of a GET (link) is not a proper solution for this threat. It would have fix the case for the crawler but a hacker could have done de same using POST method.

    Use Get or POST when ever you want to. Just remember de difference and the guidance from the w3c: get is for navigation, post is for data.

    my 2 cents
  • Laguana 2009-04-08 01:52
    Gretta:

    And oh yeah, GETs should be idempotent. If you didn't know that already, kindly remove your stinking excrement from my web.


    But deleting the whole website is idempotent, unless it fails when there is nothing left to delete...
  • Zatanix 2009-04-08 02:35
    rfsmit:
    brazzy:
    I don't believe you can call yourself a web developer until you've built an app that uses hyperlinks for deletion and have all your data deleted by a search bot.

    That explains a lot. Just don't tell the Web developers who know how to capitalize properly.

    Oh please, for the love of god... That is so extremely mind-numbingly irrelevant..

    Why oh why can't grammar people just stay at their own linguistics forums and keep away from any technical debates on the web? The signal-to-noise ratio isn't exactly increased by their continuous super-relevant insights.

    [sorry, had to blow off some steam - am happy again now]
  • bjolling 2009-04-08 03:35
    nico:
    Franz Kafka:
    Not all spiders declare themselves, and anyway, how would you tell them apart? Best thing to do is actually protect delete links.


    Well... that's what I was saying. The spider will not see the link at all. I guess you need to be a registered user, probably with high privileges, in order to delete stuff. Definitely a spider does not fall in this category, whether it declares itself or not... so just don't show it the link (as you don't show it to guest users or to normal users that can't delete stuff)
    And what happens if an authorized user copies the delete link to his personal webpage? You know, for quick access and stuff.
  • brazzy 2009-04-08 04:11
    GrandmasterB:
    You just beat me to this. If the fact that its a GET vs a POST matters to your app, you're doing something very wrong. Properly check the user credentials before doing the deletion, and you will not have this problem, period.


    Until, that is, a perfectly legitimate and fully authorized user installs a browser plugin to preview links or speed up browsing by prefetching them.
  • brazzy 2009-04-08 04:13
    diaphanein:
    brazzy:
    I don't believe you can call yourself a web developer when you've built an app that uses hyperlinks for deletion and have all your data deleted by a search bot.

    FTFY

    I don't know about you, but I consider experience gained through personal mistakes immeasurably more valuable than a conviction that you're too good to make mistakes.
  • plaga 2009-04-08 04:19
    Satanicpuppy:
    Mind you, I hate crap like the example. If you do "If($auth){...} you're going to get screwed by someone who understands cookies. You need to run an actual sanity check on the cookie data.

    Which is what you should do for all data anyway!
  • derula 2009-04-08 05:14
    I got hacked by Google once. It turned out to be code roughly equivalent to this one:

    if (!isset($user) || (array_key_exists($user, $passwords) && $passwords[$user] == $password)) {
    
    echo "congratz you're admin";
    }

    Please do not ask why I thought this would be a good idea, I did not know either when I finally figured it out.

    Needless to say, this page doesn't have confirmation forms for deletion, because I didn't want to go through that trouble.
  • Chip 2009-04-08 05:34
    Madball:
    This brings back memories. Had almost the same exact thing happen several years ago. We had a a backdoor admin page for a supplier "portal" for creating announcements. It had convenient little edit/delete links next to each pre-existing posting. We even had some nice internal documentation which spelled out where and how to create announcements. All was well with the world for a couple years (security was not really a concern).

    Until, the infrastructure guys bought a google search appliance. It dutifully found the documentation -> backdoor -> followed each delete link. It took some effort to track it down. After some chuckling, we started blaming google for all future mysterious occurences.


    In my much younger (and much more naive) days, I was involved in developing a custom CMS for a small online shop. One day we ran Xenu link checker on it, which obligingly followed all the unguarded "delete" links and wiped the whole stock database.

    Doh!
  • gilhad 2009-04-08 05:42
    Ryan:
    Or.. stop using if (!auth) and instead put if(auth) at each page.

    A crawler won't auth, so they get nothing.


    That is bad aproach - the script should exit as soon as possible.
    if (!auth) {.. exit"} on begin of the page is clear visible

    if(auth){
    some long stuff with many other if(something){
    }} //oops misstype, i want only one level down
    delete_all()
    } // why i get syntax error here, after deleting everything?

    is much harder to keep
  • gilhad 2009-04-08 05:42
    Ryan:
    Or.. stop using if (!auth) and instead put if(auth) at each page.

    A crawler won't auth, so they get nothing.


    That is bad aproach - the script should exit as soon as possible.
    if (!auth) {.. exit"} on begin of the page is clear visible

    if(auth){
    some long stuff with many other if(something){
    }} //oops misstype, i want only one level down
    delete_all()
    } // why i get syntax error here, after deleting everything?

    is much harder to keep
  • anon 2009-04-08 05:50
    jonnyq:
    Just make sure you die() before doing anything else.


    This is what I will be telling everyone today..
  • puppa! 2009-04-08 05:53
    Does everybody here know what is an header, and what the client can do with it, if the true meaning is an attack?
  • Watson 2009-04-08 06:12
    nico:
    Franz Kafka:
    Not all spiders declare themselves, and anyway, how would you tell them apart? Best thing to do is actually protect delete links.


    Well... that's what I was saying. The spider will not see the link at all. I guess you need to be a registered user, probably with high privileges, in order to delete stuff. Definitely a spider does not fall in this category, whether it declares itself or not... so just don't show it the link (as you don't show it to guest users or to normal users that can't delete stuff)


    Well, that's what the validation code was for, wasn't it? If you're not authorised to view the page you get redirected back to the index....

    ...Unless you're one of those clients that ignore Location: headers.

    if(!isset($_SESSION['usr_id']) || !isset($_SESSION['usr_name']))
    {
    header('Location: index.php');
    }
    // display big long list of delete links, e.g.,
    echo '<a href="/theCMS/pageEditor.php?action=deletePage&pageId=94">Kill Page 94'</a>';

    Spider hits this code, it's not authorised, so it gets a Location header (which it ignores) followed by a big long list of delete links, which it blindly starts following. It's a good bet that the validation mechanism on subsequent pages is the same.

    Even if the Location: header is respected by the client, there is nothing in the call to header() that terminates the script's processing. The server still goes to the effort of generating that big long list of delete links: I guess it's only blind luck (to put it charitably) that prevented anyone from seeing what would happen if an unauthorised user tried to access the page that actually performs the deletion. Prior, that is, to it happening in the wild. (In other words: no, it's not "all well and good if the client respects the Location header").

    The only difference caused by the client ignoring "Location:" is that it didn't ignore the content that followed (which according to spec is supposed to offer alternative URIs for the requested resource anyway).
  • biff 2009-04-08 06:19
    "..differentiate the difference..."????

    wtf?
  • Arie Kanarie 2009-04-08 07:00
    Harold:
    if(!isset($_SESSION['usr_id']) || !isset($_SESSION['usr_name']))
    {
    header('Location: index.php');
    exit()
    }

    would have been the quick fix.


    If you consider producing a parse error to be a quick fix....

    DoH!
  • gherkin 2009-04-08 07:20
    This is not just a PHP WTF - it's an HTTP WTF.

    There are too many web developers who don't understand the basic tenets of HTTP. Including simple things such as, Location headers don't force the client to do anything.

    I've tried to explain this before and been met with blank stares (or at least I imagine the people on the other end of the interwebs are staring blankly) when pointing out that following headers is technically entirely optional for browsers.

    Of course, the PHP should have included an exit() or die() after the header() line - but you don't understand this until you understand HTTP.
  • Anonym 2009-04-08 07:31
    A similar thing happened to a friend of mine. He had to (for some reason) remove the security on his phpMyAdmin for a few minutes. Of course, just then, google wanted to index his page. *poof*, no data.

    Extreme timing, i must say.
  • dman 2009-04-08 07:35
    Is this a WTF-classic, or just a re-run of "Google ate my website" http://thedailywtf.com/Articles/The_Spider_of_Doom.aspx ?

    ...
    and to rfsmit :
    Folk in this decade who feel the need to capitalize "The Web" or "The Internet" are the same folk who still use phrases like "The Cancer", "The Aids" or "The Iraq".
    Once someone understands how it works, they stop calling devices the "Tele-Phone" or "Auto-Mobile".
    You'll get there eventually.
  • Old Git 2009-04-08 08:46
    rohypnol:
    ckoppelman:
    or just add rel="nofollow"

    Yeah, that'll teach those perky script kiddies to stay away from your website!
    rel="nofollow getoffmylawn"
  • Homer Jay 2009-04-08 08:49
    dman:
    Once someone understands how it works, they stop calling devices the "Tele-Phone" or "Auto-Mobile".
    You'll get there eventually.


    "Tele-ma-phone". "Auto-ma-mobile". Aww, that was easy!
  • Dr. Bunsen Honeydew 2009-04-08 08:52
    biff:
    "..differentiate the difference..."????

    wtf?
    It means to measure the rate of change of the difference between "edit" and "delete" with respect to time, or some other variable.

  • nico 2009-04-08 09:27
    And what happens if an authorized user copies the delete link to his personal webpage? You know, for quick access and stuff.


    Nothing, because you also have the check in the page that actually issues the DELETE.

    I was just saying that if you start by putting the check in the frontend page, where you have the link to the DELETE page you're even less prone to this kind of disaster, and you don't expose the page address to everyone.
  • jnareb 2009-04-08 09:34
    Captain Oblivious:
    If you rely on a form button to delete content, also remember to use the POST method, not GET!

    What he said!

    Additionally, there is also robots.txt which one can use...
  • PHPer 2009-04-08 10:51
    I never use the location header for just this reason

    Captcha explains it all: validus
  • cracky 2009-04-08 10:59
    Wow - 1998 called. It wants its WTF back.
  • Paul 2009-04-08 13:22
    Reminds me of the webmail application I worked on in the late 90s.

    I discovered if I "cleverly" crafted an HTML email with a couple of img tags in it, like:
    <img src="/delete?mbx=INBOX">
    <img src="/emptyTrash">

    I could delete most of another users Inbox (not necessarily all since the emptyTrash might have kicked off before the delete was finished).

    After a bit more investigation I found I could do pretty much everything via img tags. For instance, something like this:
    <img src="/setFwd?addr=myaddress@baddy.com&desc=&name=">

    would add a forwarding address to the account. Since desc and name were empty, it wouldn't show up in the forwarding address section of the user's profile.

    Or I could fill an email with a couple thousand:
    <img src="/send?to=someaddress1@domain.com&subj=SPAM&msg=Buy%20Your%20SPAM%here">
    <img src="/send?to=someaddress2@domain.com&subj=SPAM&msg=Buy%20Your%20SPAM%here">
  • Coward 2009-04-08 13:24
    QFT
    jonnyq:
    The article is a bit misleading about headers and PHP, but not off by much.


    $authorized = authorize(); // let's assume this is false
    if(!$authorized) {
    // this, by default, sets a 302 status header
    header("Location: index.php");
    // because I'm stupid, this was omitted
    // die();
    }

    // this works. PHP happily continues after the header() call
    deleteSomething();

    // this fails. Since the 302 status header is set, PHP is going to die() before sending any actual output.
    echo "Hello World!";

    // this fails, too. PHP done died.
    deleteSomethingElse();


    So, setting a Location header on a failed auth is OK. Just make sure you die() before doing anything else.

    Most robots/spiders handle 30x redirects just fine. It's just that PHP doesn't die() immediately until you try to output something after that header was sent.

    Yes, using a <form> would keep the spider away, but that won't keep wrongdoers at bay. Yes, deletes should be done with a POST and not a GET, but that's less important than just making sure the damned script dies when auth fails.
  • jordanwb 2009-04-08 14:02
    Paul:
    Reminds me of the webmail application I worked on in the late 90s.

    I discovered if I "cleverly" crafted an HTML email with a couple of img tags in it, like:
    <img src="/delete?mbx=INBOX">
    <img src="/emptyTrash">

    I could delete most of another users Inbox (not necessarily all since the emptyTrash might have kicked off before the delete was finished).

    After a bit more investigation I found I could do pretty much everything via img tags. For instance, something like this:
    <img src="/setFwd?addr=myaddress@baddy.com&desc=&name=">

    would add a forwarding address to the account. Since desc and name were empty, it wouldn't show up in the forwarding address section of the user's profile.

    Or I could fill an email with a couple thousand:
    <img src="/send?to=someaddress1@domain.com&subj=SPAM&msg=Buy%20Your%20SPAM%here">
    <img src="/send?to=someaddress2@domain.com&subj=SPAM&msg=Buy%20Your%20SPAM%here">


    lol nice.
  • Pedal to the Metal 2009-04-08 14:33
    This is exactly why 'web accelerators' never took off (for example, the one from [a large search company with a funny name]), some online 'forums/webhosting/etc.' had/have "Delete Account" as a hyperlink (good bye account!)...also could be one of the lesser reasons why there's the presence of Captchas on web pages today...
  • Simon 2009-04-08 15:44
    "Location" is only meaningful if the response code is 3XX or 201.


  • Simon 2009-04-08 15:47
    never mind... IA is probably doing the right thing; the Location isn't being ignored; it's just too late.
  • Wyrd 2009-04-08 15:49
    jonnyq:
    The article is a bit misleading about headers and PHP, but not off by much.
    /* code snipped out */

    So, setting a Location header on a failed auth is OK. Just make sure you die() before doing anything else.

    Most robots/spiders handle 30x redirects just fine. It's just that PHP doesn't die() immediately until you try to output something after that header was sent.

    Yes, using a <form> would keep the spider away, but that won't keep wrongdoers at bay. Yes, deletes should be done with a POST and not a GET, but that's less important than just making sure the damned script dies when auth fails.

    Thanks for explaining this clearly. I haven't done any real web development and I was having a hard time understanding why the article was arguing for POST instead of GET, but most of the commentors were arging for a die() to be inserted. Now it makes sense. :-)

    Also, thanks for showing me the BBCode "code" tag. (Yeah, all I had to do was click BBCode Okay, but I didn't.)

    --
    Furry cows moo and decompress.
  • chrismcb 2009-04-08 19:12
    rfsmit:
    brazzy:
    I don't believe you can call yourself a web developer until you've built an app that uses hyperlinks for deletion and have all your data deleted by a search bot.

    That explains a lot. Just don't tell the Web developers who know how to capitalize properly.


    So you are saying brazzy shouldn't tell himself?
  • hobart 2009-04-09 08:44
    gilhad:
    Ryan:
    Or.. stop using if (!auth) and instead put if(auth) at each page.

    A crawler won't auth, so they get nothing.


    That is bad aproach - the script should exit as soon as possible.
    if (!auth) {.. exit"} on begin of the page is clear visible

    if(auth){
    some long stuff with many other if(something){
    }} //oops misstype, i want only one level down
    delete_all()
    } // why i get syntax error here, after deleting everything?

    is much harder to keep


    Maybe use better laid out code?


    if(auth)
    {
    some long stuff with many other if(something)
    {
    }
    delete_all();
    } // Close brackets lined up with open brackets - should not be difficult to spot mismatch


    Or, even better, try using functions


    if(auth)
    {
    do_deletion();
    } // Close brackets lined up with open brackets - should


    do_deletion()
    {
    some long stuff with many other if(something)
    {
    }
    delete_all();
    }


    As far as I can tell things like
    exit()
    are nothing more than rebranded gotos, with all of the problems that they bring. For a simple one-line test at the top of the page, it may not be too much of an issue but if you start scattering them throughout your code as soon as you've reached a certain point, then trying to check whether a particular bit of code is going to be executed can be a nightmare.
  • David 2009-04-09 08:57
    Yup that should have worked.
    I also sometimes use a javascript redirect (document.location) and for the noscript-ers beyond us I would've made a clickable hyperlick to the index.php.
  • nf 2009-04-09 08:59
    header("location: someurl");
    exit; // fixed.
  • Matt Bernier 2009-04-10 11:00
    Exactly! If you put exit; or exit() you will always STOP execution of the script immediately. PHP will not continue to execute and even robots will only see a blank unfunctional page.
  • Darkstar 2009-04-10 12:03
    Keep that in mind when designing your systems; use <input type="button" value="Delete Something" />, not <a href="delete.php?deleteWhat=everything&deleteMode=unrecoverable">index me!</a>. And maybe don't use just the Location header to secure your site.

    Now there is yet another WTF. The server must always enforce the access control model. Changing the way the client talks to the server doesn't solve anything.
  • tgape 2009-04-11 18:10
    hobart:
    As far as I can tell things like
    exit()
    are nothing more than rebranded gotos, with all of the problems that they bring. For a simple one-line test at the top of the page, it may not be too much of an issue but if you start scattering them throughout your code as soon as you've reached a certain point, then trying to check whether a particular bit of code is going to be executed can be a nightmare.


    The main problem with goto is that it isn't easy to tell exactly where it is going. Also, it's flexible enough that one can get truly horrendous flow control.

    for and while are rebranded goto. They limit the flexibility enough that one has a great deal of difficulty making the sort of gordian knot that was common back in the days of goto.

    exit is a rebranded longjmp. It has the saving grace that there's only one place that it will longjmp to.

    While having multiple return or exit points in your subroutines or web page may make things difficult, it's nowhere near what goto (and gosub) once did.
  • tgape 2009-04-11 18:19
    Jay:
    This is like those stores that put up "No weapons allowed" signs with the big red slash through a picture of a handgun. Like, yeah, some maniac is going to load up with guns and bombs and head out to the mall with the intention of killing as many people as he can before the police finally arrive to stop him, he's prepared to sacrifice his own life for the pleasure or glory or whatever he gets out of killing innocent people ... and then he's going to see a "No weapsons allowed" sign on the door and say, "Oh, darn, I guess I can't do that here."


    Actually, those signs are much more about allowing the store or mall security types to treat anyone with detectable weapons as a gun-toting maniac - thus giving them a chance to deal with said maniac before he starts hurting people.

    Now, thinking such a sign will keep out the maniacs, allowing one to not *have* in-store security is a serious WTF. However, to my knowledge, I've only encountered one store manager that delusional.

    Just because you don't *see* the store security types doesn't mean they aren't there. Some savvy stores have one unit on duty watching the store cameras, a uniformed unit or three on call, and a few plain-clothes guards roaming the store, looking like customers. *Really* savvy stores will also have a uniformed guard near each exit, in addition to the hidden security.
  • tgape 2009-04-11 18:27
    Eevee:
    I'm a little disturbed that several people now have condoned wrapping almost an entire file's worth of code in an if() block. Flow control is your friend.


    I only saw one before this post. Most of the prior posts indicated using die or exit in the beginning if block. That only equates to wrapping the entire file in an if block if you're one of those purists who feels that each section of code should only have one return or exit point.

    For what it's worth, while I do agree that one should limit the number of exit points for any subroutine, it usually makes code less legible to carry this to the extreme. I find it tends to work better to try to keep early returns at the very beginning. If one really needs to have a large block of code, a potential exit point, and then another large block of code, that probably should be refactored into two subroutines, which are invoked by a simple subroutine which calls the first, and then conditionally calls the second.
  • tgape 2009-04-11 18:35
    Edgeman:
    And that's why header redirects like this need a die(); afterward... even if you never expect the code to hit it, better to be safe than sorry.


    This is a fundamental misunderstanding of header redirects. As other posts have mentioned, specifying that there should be a redirect header does *not* stop processing. If there is a die immediately after a header redirect, the code will *always* hit it, and your error logs will fill up with useless "errors" which are, in fact, statements of the web page actually working as designed.

    Debugging your web pages will go much easier if you keep stuff like this out of your logs. That way, when you have problems, you can go to your logs, and actually find *real* problems.

    (I'm submitting this response, because nobody else has indicated why one should use exit instead of die - people have only stated that one or the other should be used. It's not just a meaningless preference thing. Using one is poor practice, using the other good practice. Either is far, far better than having completely broken code like, well, the "Well-Intentioned Destruction" above.)
  • gg 2009-04-12 04:37
  • csrster 2009-04-14 06:43
    I work for a national internet archiving consortium. We are legally obliged to ignore robots.txt.
  • pink_fairy 2009-04-14 16:37
    csrster:
    I work for a national internet archiving consortium. We are legally obliged to ignore robots.txt.
    Well, nobody else is going to ask this question at this point, because here on TDWTF we value immediacy and sad self-gratification above knowledge.

    But why? And by what law?

    I mean, I understand ignoring robots and crawlers and stuff; that's just common sense.

    But exactly what law mandates you to invade personal privacy when there's a Big Ole Warning Sign outside saying "Chien Mechant! Chatte Lunatique!"

    May your idiot bosses be infested with bot flies. (Yes, I'm aware that the suggestion is slightly Alanis Morrisette^W^W ironic.)
  • Willllllllllllllllll 2009-04-15 03:01
    DON'T
    use headers to secure your web application.

    Just don't.
  • rong1982 2009-04-23 12:05
  • rong1982 2009-04-23 12:05
    搬家搬家 搬家公司在職進修 婚紗 新娘秘書 汽車旅館 彩妝造型 新娘秘書 票貼 室內設計 室內設計 外遇 抓姦 應收帳款 徵信 徵信社 外遇 徵信 徵信社 外遇 植牙 牙齒矯正 坐月子 宜蘭民宿 婚禮佈置 宜蘭民宿推薦 催眠 派報 太陽能熱水器 Shade sail nike shoes 關鍵字廣告 租屋 搬家 搬家 買房子 花蓮民宿 花蓮民宿 花店 租房子 xo醬 房屋貸款 搬家公司 減肥 減重 床墊 創業加盟 團體服 學英文 英文 補習班 勞工體檢 資源回收 生日禮物 團體服 團體制服 班服 塑膠 日立家電 飾品批發 MBA 在职研究生 在职博士 电动隔膜泵 自吸泵 化工泵 离心泵 磁力泵 螺杆泵 水泵 隔膜泵 气动隔膜泵 婚禮佈置 婚禮佈置 婚禮佈置 酒店經紀 酒店經紀 班服配件 團體服配件 團體服 班服 團體服 班服 團體服 室內設計公司 室內設計公司 室內設計公司 金門高梁酒變頻洗衣機學英文
  • David Guaraglia 2009-04-23 16:01
    Well, actually the correct answer is "never delete stuff from your server on a GET request, only on a POST request, and even then only after checking the user has the permission to delete stuff". Everything else is just fixing potholes.
  • twobee 2009-05-06 20:29
    usually you also put a die() below your header() call, to be sure the script is really stopped.
  • Anon 2009-05-12 21:48
    Yeah, server stored session variables that delete after authentication of action authorization are just too hard as well.
  • few 2009-05-19 07:08


    ドルチェ&ガッバーナ
    DOLCE & GABBANA
    ドルチェ&ガッバーナ バッグ


    ドルチェ&ガッバーナ 財布


    ドルチェ&ガッバーナ ネ

    ックレス

    ドルチェ&ガッバーナ 

    サングラス

    ドルチェ&ガッバーナ リング


    ドルチェ&ガッバーナ 香水


    ドルチェ&ガッバーナ シュー


    ドルチェ&ガッバーナ アウタ


    ドルチェ&ガッバーナ インナ


    ドルチェ&ガッバーナ シャツ


    ドルチェ&ガッバーナ ジーン


    ドルチェ&ガッバーナ 時計


    ドルチェ&ガッバーナ Tシ

    ャツ


    グッチ
    gucci
    グッチ バッグ
    グッチ 財布
    グッチ ネックレス
    グッチ サングラス


    グッチ リング
    グッチ 香水
    グッチ シューズ
    グッチ アウター
    グッチ シャツ
    グッチ ジーンズ
    グッチ 時計
    グッチ Tシャツ
    グッチ アウター
    グッチ インナー


    ディオール
    Christian Dior
    ディオール バッグ
    ディオール 財布
    ディオール ネックレス


    ディオール サングラス


    ディオール リング
    ディオール 香水
    ディオール シューズ
    ディオール アウター
    ディオール シャツ
    ディオール ジーンズ
    ディオール Tシャツ
    ディオール 時計
    ディオール インナー

    ヴィトン
    LOUIS VUITTON
    ヴィトン バッグ
    ヴィトン 財布
    ヴィトン ネックレス


    ヴィトン サングラス


    ヴィトン リング
    ヴィトン シューズ
    ヴィトン アウター
    ヴィトン シャツ
    ヴィトン ジーンズ
    ヴィトン Tシャツ
    ヴィトン 時計

    シャネル
    CHANEL
    シャネル バッグ
    シャネル 財布
    シャネル ネックレス


    シャネル サングラス


    シャネル リング
    シャネル 香水
    シャネル シューズ
    シャネル アウター
    シャネル シャツ
    シャネル ジーンズ
    シャネル Tシャツ
    シャネル 時計
    シャネル インナー


    プラダ
    prada
    プラダ バッグ
    プラダ 財布
    プラダ ネックレス
    プラダ サングラス


    プラダ リング
    プラダ 香水
    プラダ シューズ
    プラダ アウター
    プラダ シャツ
    プラダ ジーンズ
    プラダ Tシャツ
    プラダ 時計



    フェラガモ
    SALVATORE FERRAGAMO
    フェラガモ バッグ
    フェラガモ 財布
    フェラガモ ネックレ


    フェラガモ サング

    ラス

    フェラガモ リング
    フェラガモ 香水


    フェラガモ シューズ


    フェラガモ アウター


    フェラガモ シャツ


    フェラガモ ジーンズ


    フェラガモ Tシャツ


    フェラガモ 時計


    セリーヌ
    CELINE
    セリーヌ バッグ
    セリーヌ 財布
    セリーヌ ネックレス


    セリーヌ サングラス



    [URL=http://celine-lukus.com/item/category/perfume]セリーヌ 香水

    セリーヌ シューズ
    セリーヌ アウター
    セリーヌ シャツ
    セリーヌ ジーンズ
    セリーヌ Tシャツ

    ボッテガ
    BOTTEGA VENETA
    ボッテガ バッグ
    ボッテガ 財布
    ボッテガ ネックレス


    ボッテガ サングラス


    ボッテガ リング
    ボッテガ 香水
    ボッテガ シューズ
    ボッテガ アウター
    ボッテガ シャツ
    ボッテガ ジーンズ
    ボッテガ Tシャツ

    コーチ
    COACH
    コーチ バッグ
    コーチ 財布
    コーチ ネックレス
    コーチ サングラス


    コーチ リング
    コーチ 香水
    コーチ シューズ
    コーチ アウター
    コーチ シャツ
    コーチ ジーンズ
    コーチ Tシャツ
    コーチ 時計


    ダンヒル
    dunhill
    ダンヒル バッグ
    ダンヒル 財布
    ダンヒル カフスボタン



    [URL=http://dunhill-lukus.com/item/category/ring]ダンヒル リング

    ダンヒル 香水
    ダンヒル シューズ
    ダンヒル アウター
    ダンヒル シャツ
    ダンヒル ジーンズ
    ダンヒル Tシャツ
    ダンヒル 時計

    ロエベ
    LOEWE
    ロエベ バッグ
    ロエベ 財布
    ロエベ ネックレス
    ロエベ サングラス


    ロエベ キーホルダー
    ロエベ 香水
    ロエベ シューズ
    ロエベ アウター
    ロエベ シャツ
    ロエベ ジーンズ
    ロエベ Tシャツ

    ディーゼル
    DIESEL
    ディーゼル バッグ
    ディーゼル 財布
    ディーゼル ネックレス


    ディーゼル サングラ


    ディーゼル リング
    ディーゼル 香水
    ディーゼル シューズ
    ディーゼル アウター
    ディーゼル シャツ
    ディーゼル ジーンズ
    ディーゼル Tシャツ


    ディーゼル 時計
    ディーゼル インナー

    デリヘル 大阪
    仙台 デリヘル
    仙台 風俗
    仙台 デリヘル
    仙台 風俗
    仙台 デリヘル
    仙台 風俗
    家族葬
    滋賀 賃貸
    葬儀 費用
    滋賀県の賃貸
    滋賀の賃貸
    アダルト SEO
    被リンク
    茶道具 買取
    絵画 買取
    レザー
    革小物
    クレジットカード 現金化
    現金化
    ショッピング枠 現金化
    クレジットカード 現金化
    現金化
    ショッピング枠 現金化
    FX
    FX 比較
    FX 初心者
    ダイエット
    ダイエット食品
    腕時計
    ヘアアイロン
    クレイツ
    アイビル
    アドスト
    バッグ
    ネイル
    アクセンツ
    脱毛 大阪
    埋没 大阪
    わきが 大阪
    オーロラ 大阪クリニック
    タウンサーチ
    探偵 大阪
    浮気調査 大阪
    素行調査 大阪
    別れさせ 大阪
    吹田 美容室
    エステ 尼崎
    メナード 梅田
    MM
    MAJORITY&MINORITY
    エムエム
    メナード 梅田


    キャッシング
    大阪 賃貸
    中古車 販売
    ルームウェア
    大阪 マンション
    賃貸マンション 神戸
    中古 ゴルフクラブ
    クールビズ
    フィットネスクラブ
    大阪府 司法書士
    クレジット 申し込み
    ベビードール
    矯正歯科 東京
    ホワイトニング 東京
    大阪 ラブホテル
    リサイクルショップ
    不動産
    カードローン
    投資 信託
    下着
    即日 キャッシング
    銀行
    神戸市 中央区 税理士
    FX
    消費者金融
    ローン
    引越し
    生命保険
    ジェルネイル
    人材派遣
    ネット証券
    アフィリエイト
    格安航空券
    ウィークリーマンション
    レンタカー
    SEO
    オフィス家具
    合宿免許
    ペット用品
    高速バス
    デリヘル
    キャバクラ
    派遣
    コラーゲン
    化粧品
    インテリア
    ウェディング
    結婚相談
    投資物件
    留学
    貸事務所 大阪
    経営コンサルティング
    工芸品
    高級品
    自動車保険
    ホテヘル
    レストランウェディング
    バイク買取
    運転免許
    ベビーカー
    外反母趾
    圧力鍋
    腕時計
    フェラガモ
    デリヘル
    キャバクラ
    セレブ
    プラセンタ
    カルシウム
    青汁
    ブルーベリー
    家具
    脱毛クリーム
    除毛クリーム
    コスト削減 大阪
    弁護士 大阪
    車買取 大阪
    バイク買取 大阪
    エステ 大阪
    リフォーム 大阪
    大阪 歯科
    派遣 大阪
    アルバイト 大阪
    転職 大阪
    大阪 住宅
    大阪 専門学校
    グルメ 大阪
    ホテル 大阪
    一戸建て 大阪
    大阪 宿泊
    大阪 マンション
    デリヘル 大阪
    印刷 大阪
    不動産 大阪
    賃貸 大阪
    ブライダル 大阪
    リサイクル
    アダルト SEO
    賃貸
    SEO 大阪
    イベント コンパニオン 大阪
    転職 大阪
    大阪 ラブホ
    ペット ショップ 大阪
    豆腐
    京都 不動産
    運転免許 合宿
    ヘアアイロン
    ダイエット
    ダイエット
    デリヘル
    キャバクラ
    シャンパン
    老人ホーム
    精力剤
    大阪 ラブホテル
    ブランド品 買取
    ワイン
    京都 不動産
    ペット
    リサイクルショップ
    歯科求人
    結婚式場
    バイク便
    動物病院
    美容整形外科
    エルメス
    ヘアアイロン
    クレイツ
    シャンプー
    アイビル
    ジェルネイル
    育毛剤
    ドライヤー
    アゲハ嬢
    ダイエットサプリ
    エステ 大阪 フェイシャル
    エステ 大阪 求人
    エステ 大阪 メンズ
    リフォーム 大阪 キッチン
    リフォーム 大阪 マンション
    リフォーム 大阪 外壁
    大阪 歯医者 ランキング
    大阪 歯医者 料金
    大阪 歯医者 矯正
    派遣 大阪 求人
    派遣 大阪 短期
    派遣 大阪 ランキング
    アルバイト 大阪 検索
    アルバイト 大阪 短期
    アルバイト 大阪 口コミ
    転職 大阪 求人
    転職 大阪 ランキング
    転職 大阪 女性
    大阪 住宅 検索
    大阪 住宅 ローン
    大阪 住宅 中古
    専門学校 大阪 美容
    専門学校 大阪 看護
    専門学校 大阪 調理
    大阪 グルメ カフェ
    大阪 グルメ お好み焼き
    大阪 グルメ たこ焼き
    ホテル 大阪 シティホテル
    ホテル 大阪 ビジネスホテル
    ホテル 大阪 モーテル
    大阪 一戸建て 検索
    大阪 一戸建て 口コミ
    大阪 一戸建て ランキング
    大阪 宿泊 格安
    大阪 宿泊 高級
    大阪 宿泊 口コミ
    大阪 マンション 新築
    大阪 マンション 中古
    大阪 マンション 賃貸
    大阪 デリヘル 人妻
    大阪 デリヘル OL
    大阪 デリヘル 3P
    印刷 大阪 チラシ
    印刷 大阪 名刺
    印刷 大阪 格安
    大阪 不動産 検索
    大阪 不動産 分譲
    大阪 不動産 比較
  • ivy 2009-05-21 22:38
    uggs, with a legendary brand, first to see the snow ugg boots ,Ugg people will not Ben Ben flu cartoon form, and is a boots, as a result of many European and American film star has adequate Gordon Street Ugg snow boots and a pretty popular in Europe and America look like the earth, Ugg blowing sustained winds of the popular Madden, in Japan, Taiwan has a lot of fans Ugg.

  • matt 2010-05-21 06:53
    年上が好きなら品川デリヘルがいいよ熟女いっぱいです。
  • jusmine 2011-07-06 02:41
    Double Bonus
    <a href="http://www.n-hisho.com" target="_blank">西武黄金時代を心ゆくまで語ろう!!</a>
    <a href="http://www.osaka-ah.com" target="_blank">一寸先は闇</a>
  • jusmine 2011-07-08 10:07
    Good night
    <a href="http://www.minamiaoyama-luxury.com" target="_blank">時は金なり!!</a>
    <a href="http://www.mjimusume.com" target="_blank">いじめについて考えよう</a>
  • jusmine 2011-07-12 08:55
    recycle things
    <a href="http://www.max-3222.com" target="_blank">北の怪物</a>
    <a href="http://www.mduma.net" target="_blank">ダラダラすることが好きです。ブログ</a>