Streaming images allows us to hide the true name and source of the images from any robot attempting to automatically pass our CAPTCHA test. I'm using files, you could use a database or another method.
This example simply demonstrates that classic ASP is quite capable of streaming images. I have also improved on his version by increasing the number of available image choices, providing easier to recognize images, and ensuring no image is used twice on a page. For a character based CAPTCHA example in classic ASP (not using streaming) see this example. For an example of reading files as an include with pure JavaScript without using an <IFRAME> or a .js extension, (another thing you supposedly can not do, see this article.)
Click the three pictures of "cats" to verify.
NOTE: All images are freely available content available from Wikimedia Commons.
<%
Option Explicit
Session.CodePage=65001
Response.Charset="UTF-8"
' no browser caching of this page
Response.Expires=-1
Response.ExpiresAbsolute = Now() - 1
' do not allow proxy servers to cache this page !! to be used on all pages
Response.CacheControl="private"
Response.CacheControl="no-cache"
Response.CacheControl="no-store"
' A temporary array used to get unique random values.
' i.e. we pick a random value, remove it from the array, and on
' subsequent random picks, we reject the value if already removed
' from the array.
Dim Unique(9)
Unique(1)=1
Unique(2)=2
Unique(3)=3
Unique(4)=4
Unique(5)=5
Unique(6)=6
Unique(7)=7
Unique(8)=8
Unique(9)=9
function getRndUnique
Dim idx
idx=0
While (idx=0)
idx = Int(8 * Rnd)+1
if Unique(idx)>0 then
idx = Unique(idx)
Unique(idx)=0
else
idx=0
end if
Wend
getRndUnique = idx
end function
function getCorrect
' choose three unique values for the image CAPTCHA
' the values will be comma separated in ascending order.
Dim tmp, idx, out
tmp = getRndUnique & "," & getRndUnique & "," & getRndUnique
for idx=1 to 9
if Unique(idx)=0 then
out = out & idx & ","
end if
next
getCorrect = Left(out, Len(out)-1)
end function
' another temporary array for getting unique images on each page
' with no repeated images
Dim UniqueImg(18)
UniqueImg(1)=1
UniqueImg(2)=2
UniqueImg(3)=3
UniqueImg(4)=4
UniqueImg(5)=5
UniqueImg(6)=6
UniqueImg(7)=7
UniqueImg(8)=8
UniqueImg(9)=9
UniqueImg(10)=10
UniqueImg(11)=11
UniqueImg(12)=12
UniqueImg(13)=13
UniqueImg(14)=14
UniqueImg(15)=15
UniqueImg(16)=16
UniqueImg(17)=17
UniqueImg(18)=18
function notUsed
Dim idx
for idx=1 to 18
if UniqueImg(idx)>0 then
notUsed = idx
UniqueImg(idx)=0
Exit For
end if
next
end function
function getRndUniqueImg
Dim idx
idx=0
While (idx=0)
idx = Int(18 * Rnd)+1
if UniqueImg(idx)>0 then
idx = UniqueImg(idx)
UniqueImg(idx)=0
else
idx=notUsed
end if
Wend
getRndUniqueImg = idx
end function
Randomize
' we use session variables so the image streaming page
' will know what image to return
Session("img1") = getRndUniqueImg
Session("img2") = getRndUniqueImg
Session("img3") = getRndUniqueImg
Session("img4") = getRndUniqueImg
Session("img5") = getRndUniqueImg
Session("img6") = getRndUniqueImg
Session("img7") = getRndUniqueImg
Session("img8") = getRndUniqueImg
Session("img9") = getRndUniqueImg
' The correct image choices will be the three numbers we put in correct.
Session("correct") = getCorrect
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="author" content="Roderick Divilbiss">
<meta name="copyright" content="© 2005, 2006 Roderick Divilbiss">
<meta name="MSSmartTagsPreventParsing" content="TRUE">
<title>Image Captcha :: Classic ASP Image Streaming</title>
<script type="text/javascript">
<!--
// to keep track of the image clicks
var clicked=new Array();
clicked[1]=false;
clicked[2]=false;
clicked[3]=false;
clicked[4]=false;
clicked[5]=false;
clicked[6]=false;
clicked[7]=false;
clicked[8]=false;
clicked[9]=false;
function updateClick() {
// put the values which are clicked into our form
var tmp='';
for (var idx=1;idx<=9;idx++) {
if (clicked[idx]) {
tmp+=idx+',';
}
}
if (tmp.substr(tmp.length-1,1)==',') {
tmp = tmp.substr(0,tmp.length-1);
}
document.getElementById('value1').value=tmp;
}
function cMe(objI) {
var idx=parseInt(objI.id.substr(3,1));
if (objI.style.borderColor=='') {
// never clicked
objI.style.borderColor='red';
clicked[idx]=true;
updateClick();
}else if (objI.style.borderColor=='black') {
// clicked after being unclicked
objI.style.borderColor='red';
clicked[idx]=true;
updateClick();
} else {
// unclicked
objI.style.borderColor='black';
clicked[idx]=false;
updateClick();
}
// auto submit on three clicks
// you could remove this and add a submit button
if (document.getElementById('value1').value.length==5) {
var myForm = document.getElementById('theForm');
myForm.submit();
}
}
//-->
</script>
<style type="text/css">
<!--
#container {
width: 350px;
height: 300px;
}
#iarray li {
float:left;
margin-right:10px;
margin-bottom: 10px;
list-style-type:none
}
.dynamicImg {
border:solid 2px;
border-color:black;
border-style:solid;
}
-->
</style>
</head>
<body>
<!-- the container division allows us to set a width-->
<div id="container">
<form id="theForm" name="theForm" method="post" action="dynamicImageVerify.asp">
<ul id="iarray">
<li><img class="dynamicImg" id="img1"
src="streamImage.asp?c=1"
alt="verification image"
onclick="cMe(this);" width="75" height="75"></li>
<li><img class="dynamicImg" id="img2"
src="streamImage.asp?c=2"
alt="verification image"
onclick="cMe(this);" width="75" height="75"></li>
<li><img class="dynamicImg" id="img3"
src="streamImage.asp?c=3"
alt="verification image"
onclick="cMe(this);" width="75" height="75"></li>
<li><img class="dynamicImg" id="img4"
src="streamImage.asp?c=4"
alt="verification image"
onclick="cMe(this);" width="75" height="75"></li>
<li><img class="dynamicImg" id="img5"
src="streamImage.asp?c=5"
alt="verification image"
onclick="cMe(this);" width="75" height="75"></li>
<li><img class="dynamicImg" id="img6"
src="streamImage.asp?c=6"
alt="verification image"
onclick="cMe(this);" width="75" height="75"></li>
<li><img class="dynamicImg" id="img7"
src="streamImage.asp?c=7"
alt="verification image"
onclick="cMe(this);" width="75" height="75"></li>
<li><img class="dynamicImg" id="img8"
src="streamImage.asp?c=8"
alt="verification image"
onclick="cMe(this);" width="75" height="75"></li>
<li><img class="dynamicImg" id="img9"
src="streamImage.asp?c=9"
alt="verification image"
onclick="cMe(this);" width="75" height="75"></li>
</ul>
<p><input type="hidden" id="value1" name="value1" size="20"></p>
</form>
</div>
</body>
</html><%
Option Explicit
Dim img, idx, param, correct
correct = Session("correct")
param = Request.QueryString("c")
' note the image URL on the form has a parameter
' which has no association with which image is streamed
' and the parameter is only valid for a particular session.
' I am choosing three of eighteen possible cats. You could
' make this exceedingly large if you were worried about
' harvesting...which I am not. A Blog is not Fort Knox.
Dim validImage(18)
validImage(0) = "NotAvailable.jpg"
validImage(1) = "cat1.jpg"
validImage(2) = "cat2.jpg"
validImage(3) = "cat3.jpg"
validImage(4) = "cat4.jpg"
validImage(5) = "cat5.jpg"
validImage(6) = "cat6.jpg"
validImage(7) = "cat7.jpg"
validImage(8) = "cat8.jpg"
validImage(9) = "cat9.jpg"
validImage(10) = "cat10.jpg"
validImage(11) = "cat11.jpg"
validImage(12) = "cat12.jpg"
validImage(13) = "cat13.jpg"
validImage(14) = "cat14.jpg"
validImage(15) = "cat15.jpg"
validImage(16) = "cat16.jpg"
validImage(17) = "cat17.jpg"
validImage(18) = "cat18.jpg"
Dim otherImage(18)
otherImage(0) = "NotAvailable.jpg"
otherImage(1) = "not18.jpg"
otherImage(2) = "not2.jpg"
otherImage(3) = "not16.jpg"
otherImage(4) = "not4.jpg"
otherImage(5) = "not14.jpg"
otherImage(6) = "not6.jpg"
otherImage(7) = "not7.jpg"
otherImage(8) = "not8.jpg"
otherImage(9) = "not9.jpg"
otherImage(10) = "not10.jpg"
otherImage(11) = "not11.jpg"
otherImage(12) = "not12.jpg"
otherImage(13) = "not13.jpg"
otherImage(14) = "not5.jpg"
otherImage(15) = "not15.jpg"
otherImage(16) = "not3.jpg"
otherImage(17) = "not17.jpg"
otherImage(18) = "not1.jpg"
function inCorrect(num)
Dim tmp, out
out = false
tmp = Split(Session("correct"),",")
for idx = 0 to UBound(tmp)
if CInt(tmp(idx))=CInt(num) then
out = true
end if
next
inCorrect = out
end function
' A little protection against calling the page without
' coming from the correct image CAPTCHA page.
' e.g. attempt to stop harvesting of images or
' associating any image with a URL.
if param & "x" = "x" then
img = "NotAvailable.jpg"
else
idx = Session("img" & param)
if (idx < 1) or (idx="") or (idx & "x"="x") then
img = "NotAvailable.jpg"
else
if InStr(correct, param)>0 then
img = validImage(idx)
validImage(idx)="NotAvailable.jpg"
Session("img" & param)=0
else
img = otherImage(idx)
end if
end if
end if
Dim objStream
Set objStream = Server.CreateObject("ADODB.Stream")
'Open a image file
objStream.Type = 1 ' adTypeBinary
objStream.Open
objStream.LoadFromFile Server.MapPath(img)
'Output the contents of the stream object
'Response.ContentType = "image/gif"
Response.ContentType = "image/jpeg"
Response.BinaryWrite objStream.Read
objStream.close
Set objStream=nothing
%>