views:

169

answers:

5

Hey all,

I have a rather complicated situation where I run a personal blog where every Friday and Sunday, I will post up music on the blog by uploading the mp3s into a folder, where a Flash mp3 player accesses it and plays it for the world.

Recently, some website called Dizzler, which is like a spider for mp3 files (Like the ones I host on my server!) and lets people play them via their own proprietary player. Now, I normally wouldn't be against other people using my server for their own gain but this recently got out of hand. In the last week of December, they managed to rack up 100k hits on one song and used up 6GB of bandwidth.

In that last week of December, I edited my .htaccess file to remove access to mp3s on my server without taking away access to my mp3s (So "deny all" isn't an option!) and I used this code:

RewriteEngine on
RewriteCond %{HTTP_REFERER} .
RewriteCond %{HTTP_REFERER} !^(www\.)?mydomain.com [NC]
RewriteRule \.(mp3)$ - [NC,F] 

Options -Indexes

It worked pretty well with one exception - it broke every Wordpress installation on my server. What I mean is that outside of the index page, if you clicked on an entry in Wordpress, it wouldn't be able to find it. My host's solution was to add "RewriteEngine on" to every .htaccess file for every installation and in the root of the web server root.

That was a great fix and all the pages work again - but it is no longer blocking my mp3 files in that folder.

What can I do?

PS. For clarification, the code above is in an .htaccess file in the folder containing the mp3s. Hope that helps!

+1  A: 

FilesMatch is the directive you need:

<FilesMatch "\.mp3$">
    Order Allow, Deny
    Allow from localhost #Or the address of your player
    Deny From All
</FilesMatch>
Vinko Vrsalovic
Actually used that before only allowing specific referrers, but it would block out my MP3 player. I also tried to block hotlinking via cPanel, but same result. Thanks though!
JonLim
See the edit, it should work now.
Vinko Vrsalovic
It may be because I don't know the exact address of my Flash player, but since it's hosted locally I would imagine localhost should work. Regardless, gave it a whirl, still blocked it out. Rather frustrating, but thanks so much!
JonLim
Does your player get a 403? Are you sure they are both in the same machine? Can't you ask your hosting provider? (Or make the player access another webserver where you have access to the logs to see the IP address of the player).
Vinko Vrsalovic
No luck, but I posted a solution below that I am testing out at the moment, thanks for all your help, definitely helped point me in the right direction. Cheers!
JonLim
this won't work as word-press mp3 players are actually flash applets that request the file from the viewing user's computer
Mala
@Mala: Right. Although there should be a pure apache approach (without needing to parse logs or write PHP code). Maybe via a cookie? That'd be easier
Vinko Vrsalovic
@Vinko Vrsalovic: Hmm... I highly doubt that the request from the flash player would send said cookie, but come to think of it I never tested that. In any case even if it did you'd need a mp3-serving php script to read the cookie and decide whether or not to serve the song, no? It would simplify matters a great deal though, true. I'll test it out and get back to you tomorrow
Mala
Nope, at least mod_rewrite can read and write cookies
Vinko Vrsalovic
oh interesting... I didn't know that. So if the player -does- send the cookies, an easy solution presents itself :) Now I feel kind of silly for spending ages writing up my epic solution below and not spending the few minutes it'd have taken to test that hehe
Mala
+1  A: 

Huge thanks to Vinko Vrsalovic for all the help, definitely helped point me in the right direction, currently using the following code:

SetEnvIfNoCase Referer www\.dizzler\.com bad_referer
SetEnvIfNoCase Referer ".*(dizzler|beemp3|skreemr).*" BlockedReferer
SetEnvIfNoCase REMOTE_ADDR ".*(220.181.38.82|202.108.23.172|66.232.150.219).*" BlockedAddress

# deny any matches from above and send a 403 denied
<FilesMatch "\.mp3$">
    order deny,allow
    deny from env=bad_referer
    deny from env=BlockedReferer
    deny from env=BlockedAddress
</FilesMatch>

Testing it out tonight, will report back tomorrow if it works!

JonLim
Glad I was of help. This is a problem with Stack Overflow, it's hard to do debugging and collaborative problem solving. A problem by design, but still a problem :)
Vinko Vrsalovic
Definitely didn't solve it - still a lot of people were able to access the files. Guess there is no real way to win. :(
JonLim
After adding something about the Wildfire app from Gigya, my usage has dropped significantly as well. But this could just be a periodic dip in usage from Dizzler and Wildfire...
JonLim
A: 

I think my other answer is much better, but this is still worth considering

Reading through some of the answers, I am struck by another idea: Have your page log the IP addresses of all visitors to your site within the last two (or however many) hours. Then, create a job that gets run ever 2 seconds or so which rewrites your .htaccess file to only allow access to mp3 files to those IP addresses in the log.

That way, only those users who have been served a page from your website in the last two hours will have access to your music. This, for the vast majority of people finding your mp3s in audio search-engines, will prove to be false.

Mala
+1  A: 

I'm posting this as another answer instead of adding this to my other post because it approaches the problem from a different angle. Here I am assuming that all your mp3s are in the same folder.

The problem you are facing is due to sloppy coding on the part of whoever made the media-player thing that wordpress uses. What happens is that the player runs on the visiting user's machine, and actually downloads the mp3 and plays it locally. The problem arises because the player does not provide any useful headers at all: the useragent is that of your browser, the referrer is blank, etc. As such, it is completely impossible to tell if the request is coming from the player, or from a browser that clicked your link in an audio search engine. Really, the only way to protect your mp3s from being indexed is to change the link as often as possible.

Which is precisely the plan. In a nutshell, here is what we are going to do:

  • change the path to your mp3s. This stays SECRET.
  • create a script to proxy for the mp3s, which requires a valid key which changes every hour
  • change all your uses of the mp3 player to use the mp3 proxy script but with a placeholder key
  • create a script to proxy for your webserver, which replaces the key placeholder with the actual key
  • use .htaccess to rewrite all requests to your server to use the webserver proxy script.

The upshot of all of this is that your user experience will not change, but if a crawler crawls your links, they will only be valid until midnight of that day, at which point requests to that url will result in a snippy message (or even an mp3 of you asking them to please not download your stuff).

Ready? OK, lets go!


Step 1:

First things first, make sure you renamed your mp3s folder! This will break all existing links (and failing to do this will mean all the links already crawled will remain valid). Secondly, create a robots.txt file to stop google and other search engines from indexing your mp3s folder.

Now, create a file in your root directory called mp3serve.php with the following contents:

<?php

/* This script checks 'key', and if it's valid, serves the mp3
 * A valid key is defined as the md5 of the current date in
 * yyyy-mm-dd-hh format concatenated with the string
 * "Hello there :)"
 *
 * The key can be anything so long as we are consistent in this
 * and the viewer proxy thing we're going to make.
 */

// edit this variable to reflect your server
$music_folder = "/new/path/to/mp3s/";

// get inputs of 'file' and 'key'
// 'file' should be the filename of the mp3 WITHOUT the extension
$file = $_GET['file'];
$key  = $_GET['key'];

// get todays date
$date = date("Y-m-d-H");

// calculate the valid key
$valid = md5($date+"Hello there :)");

if ($key == $valid)
{
    // if the key is valid, get the song in the path:
    print(file_get_contents("$music_folder/$file.mp3"));
}
else
{
    // if the key is invalid, print an admonishing message:
    print("Please don't try to download my songs, poopface.");
}

?>

What this does is it takes the filename of an MP3 and a key of some kind, and serves the file contents if the key is valid. Note that this script:

  • makes no checks at all that $file points to what you expect it to, other than the fact that it tries to make sure it will only ever return mp3 files.
  • does not return valid headers for mp3 files - they'll render as text in a browser. This is easy to fix but the correct header eludes me for the moment... and anyway the wordpress mp3 player doesn't care, so it's all good :)

Step 2:

Now for the slightly tricky part: we have to rewrite the links dynamically. The easiest way to do this is to write a "local-proxy" thing, which really is a lot easier than it sounds. What we will do is write a script that gets what your page would have outputted and corrects the mp3 links. In my example we will edit all of your articles with mp3s in them, but if you want to get fancy this is not completely necessary.

First, edit all of your articles with mp3-players in them. You could automate this, but unless WP has a "find/replace in all articles" function I would advise against it for the sole reason that you might screw up and destroy your articles. In any case, edit them and replace the mp3 links in the players from
/path/to/mp3s/<filename>.mp3
to
/mp3serve.php?file=<filename>&key=[{mp3_file_key}]

Now, create another php script in your root directory called proxyviewer.php with the following contents:

<?php

/*
 * The purpose of this file is to act as a proxy in which we can dynamically
 * rewrite the page contents. Specifically, we want to get the page that the
 * user WOULD have seen, and replace all instances of our key placeholder
 * with the actual correct key
 */

// get the requested path
$request = $_GET['req'];

// get what the source output WOULD have been
// NOTE: depending on your server's config, you -might- have to
//   replace 'localhost' with your actual site-name. This will
//   however increase page-load times. If localhost doesn't work
//   ask your host how to access your site locally. To clarify,
//   maybe show him this file.
$source = file_get_contents("http://localhost/$request");

// The reason we need to pass the request through apache (i.e. use the whole
// "http://localhost/" thing is because we need the PHP to be rendered, and
// I can't think of another way to do that using the original request uri

// calculate the correct key
$key = md5(date("Y-m-d-H")+"Hello there :)");

// replace all instances of "[{mp3_file_key}]" with the key
$output = str_replace("[{mp3_file_key}]",$key,$source);

//output the source
print($output);

?>

Step 3:

Now for the last part: set up your .htaccess file to redirect all requests from
http://yoursite/some/request/here
to
http://yoursite/proxyviewer.php?req=some/request/here

Unfortunately I'm really not good with .htaccess files so I won't be able to give you the exact code, but I imagine it shouldn't be too hard to do.

Congrats, you're done!

Disclaimer:

Please note that the code in here is not production-level code. First of all, I haven't tested it at all - although unless there's a typo somewhere they should all work, I would advise you to look through them carefully before going live with them. I have been fairly careful not to allow any Bad Things to happen, but it doesn't do any serious checking, and it's the wee hours of the morning here so I may have overlooked something.

Mala
Whoa - talk about complicated. THANKS! I'm going to give it a shot but it might take some time to test since it is a bit complicated and I still have to figure out how to change my mp3 player to access files a different way. I'm going to upvote this and hope for the best - and hope that this all works. Thanks again!
JonLim
hold on a sec... before you go to all that trouble let me test one more thing - I was having a discussion with Vinko Vrsalovic in some comments that I can't seem to find now, but there may be a much easier way. Give me another day or two to test things out, I'll get back to you
Mala
A: 

The Filesmatch solution provided above by JonLim solved the beemp3.com problem for me.