Tuesday, 17 September 2013

How to set up Twitter's "view summary" cards to work with Blogger posts

This article shows how to install Twitter Cards into Blogger - and explains why you might do this if Twitter could be an important source of visitors for your blog.


What are Twitter Cards

Recently, Neil Patel explained why having social sharing tags installed into your blog can be important, and I've written a little more about it specifically for Facebook and Blogger here.

Twitter, for reasons best known to themselves, have developed their own version of social media meta-tags, called "Twitter Cards".    (Apparently they do make some use of Open Graph tags - but not for Twitter cards displays.)


Two things happen inside Twitter when someone tweets a message including a link to a website or blog that has Twitter-cards installed.  

Firstly, the message has the words "View Summary" under it, instead of just "Expand".





Secondly, when someone in Twitter clicks the View Summary link, more information (ie a "Twitter Card") is shown about the contents of the link - like this:




In his post, Neil Patel also stated that if you don't use Wordpress,
"you’ll need to manually generate meta tags for each page on your site"
but fortunately for Blogger users who are brave enough to edit their template that's not quite true.


How to install Twitter Cards into a blog made with Blogger

There are two simple steps needed to set up Twitter sharing tags for your blog:
  • Adding the code to your template, and then 
  • Asking Twitter if you've got it right.    
The 2nd step is necessary because (for whatever reason) Twitter won't use the tags you have installed until you've tested them in Twitter's own validation tool.


Step 1   Add the Twitter Card meta-tags to your template


Edit your template in the usual way.


Find the   </head   statement, and just before it add the following lines of code:

<!--  START - TWITTER CARD TAGS   -->
<meta name="twitter:card" content="summary"/> 
<meta name="twitter:site" content="@YOUR-TWITTER-ACCOUNT-NAME"/> <meta name="twitter:domain" content="YOUR-BLOG-URL"/>

<b:if cond='data:blog.pageType == &quot;item&quot;'><meta name="twitter:title" expr:content='data:blog.pageName'/><b:else/>
<meta expr:content='data:blog.homepageUrl' name='twitter:url'/>
<meta expr:content='data:blog.pageTitle' name='twitter:title'/></b:if>
<b:if cond='data:blog.postImageThumbnailUrl'><meta name="twitter:image:src" expr:content='data:blog.postImageThumbnailUrl'/><b:else/><meta name="twitter:image:src" content='URL-FOR-IMAGE-YOU-WANT-TO-USE-IF-THERE-IS-NOT-A-THUMBNAIL-PHOTO-IN-THE-POST' /></b:if> 
<b:if cond='data:blog.metaDescription'><meta name="twitter:description" expr:content='data:blog.metaDescription'/><b:else/><!-- Still looking for a way to use the post snippet if there's no description --></b:if>

<meta name='twitter:url' expr:content='data:blog.canonicalUrl'/>
<!--  END - TWITTER CARD TAGS   -->


Except, you need to replace a few items with your own values:
  • YOUR-BLOG-URL - with your blog's address (eg for me, it's blogger-hints-and-tips.blogspot.com)
  • YOUR-TWITTER-ACCOUNT-NAME - with the Twitter account name for your blog. (This line is optional)
  • URL-FOR-IMAGE-YOU-WANT-TO-USE-IF-THERE-IS-NOT-A-THUMBNAIL-PHOTO-IN-THE-POST - with the web-address of an alternative picture to use if the post doesn't have a thumbnail image.

Save the template changes.


(Twitter also have a code-generator - but it's for websites in general, while I have configured the code above to use some of the values that Blogger makes available to us.)


Step 2   Validate your domain


After you have done the first step, go  https://dev.twitter.com/docs/cards/validation/validator.  This is Twitter's validating tool, where they check if your code meets their requirements.


Log in using your Twitter account.  
You do need to have a Twitter account yourself - or at least one that is dedicated to the blog - to use the validator and thus to install Twitter Cards.


Click the Validate and Apply tab.


Enter the address of a post from your blog and press Go.


If you're using a browser that supports showing Twitter Cards, then a preview of the card for your post will be shown in the right side of the screen.   Check that this looks correct.


Look at the list of results of your Twitter-card values shown on the left of the screen.   If any of them show a red-dot, then there is a problem that you need to fix.   Typically this will be because you've accidentally left out a quote mark when you were adding your custom values.



Fix any problems, and enter the blog-post URL again - keep going until you get a green dot at the top of the list.    (Some of the twitter card values are option, so it doesn't matter if they show as grey because they're irrelevant for a Blogger site.)


Enter the URL of your blog overall  (ie not a specific post).
  • If you do nothave a custom domain (ie your blog is  myBlog.blogspot.com), then make sure you enter the blogspot.com URL, not the country-specific one (eg   myBlog.blogspot.in).   This is important later in the validation process.
  • Fix any problems for this as well.   (There shouldn't be any, but I think it's worth double-checking, especially if you modify the twitter-cards code in any way.)


Press the Request Approval button at the top of the left hand sidebar.


Confirm the administrative details on the screen that opens - by default it will be filled in with details from your Twitter account.   You may be asked for:
  • Contact information for the person responsible for administering cards on your website (name, email address, Twitter handle)
  • Website information:   the URL (ie the domain), and a description.   Note:  if you are based outside the USA and don't have a custom domain, then most probably your country-specific address will be shown here.    Change it to the blogspot.com   address.
  • Whether your site publishes images or videos that may contain sensitive content (eg nudity, violence, or medical procedures) - so that Twitter can warn viewers before showing them.
  • The website's Twitter-name.




Press Submit Request.


After a moment, if your details are correct, Twitter shows a message saying 
"Thanks for applying to be part of Twitter's cards service. We'll review your request as soon as possible. Expect a few weeks for turn-around time. You will receive an email when your request has been reviewed."

I'm not sure if they apply this to all (or indeed any) countries or Twitter accounts:   when I installed Twitter Cards for this site, I got an email in a few minutes saying .
Your Twitter card is ready!
We've activated the summary card for blogger-hints-and-tips.blogspot.com.
If you want to use other kinds of Twitter cards (and we know you do), please make another request.

And the cards themselves were activated on a test-tweet that I did a few minutes after that.


What your readers see

If you have installed the Twitter Cards correctly, your current readers should see nothing different when they visit your blog or when they read your posts via email or and RSS feeder.

But when they include a reference to your blog in something that they send out inside Twitter, the content that they (and their followers) see is a nicely formatted card rather than an ugly-url.





Troubleshooting


Search Descriptions

Twitter cards will only work properly if you have enabled Search Descriptions for your blog, and if you have entered one for every post that is tweeted.    I looked for ways around this using the post-snippet, but haven't found a way to make this work yet.


Country-specific redirects

Neil Patel suggested one tag that is not included in the standard Twitter Cards documentation: twitter:url

Using it gets around the problems associated with country-specifc URLs for blogspot domain blogs, by changing any Tweets of them to the blogspot.com page, instead of having your tweets split across multiple urls.

I've included it in my list of tags, customized to take its value from Blogger.    However I'm not yet 100% sure if it will work - and will update this article accordingly.


Pictures

I've set up the image tag to use the thumbnail picture for each post - because that is the only one that you can access on a systematic way for each post.

Twitter's rules say that pictures must be less than 1mb in file size, at least 60px by 60px, and that ones larger than 120px by 120px will be resized.    However Blogger may have a thumbnail photo for some of your posts that is less than 60-by-60.   For these it is likely that your default image will be used instead.

The only way to over-ride this is to use a post-specific Twitter meta-tag which points to a larger photo like:
<b:if cond='data:blog.postURL == &quot;URL-OF-THE-POST&quot;'><meta name="twitter:image:src" content='URL-FOR-IMAGE-YOU-WANT-TO-USE-FOR-THIS-POST' />
</b:if>

Domains

Twiter's documentation was initially a little sketchy about which specific domain should be validated. Some people reported having to validate all three possible URLs, ie
www.your-blog.blogspot.com
your-blog.blogspot.com
/*your-blog.blogspot.com
although it is possible that this has now been resolved.


What other problems have you encountered with Twitter Cards?




Related Articles:


Adding Facebook's Open Graph tags to your blog

How to edit your template


How to embed a youtube playlist on your blogger blog

In this tutorial you will see how to create a YouTube video gallery using jQuery and add the Youtube playlist to your Blogger blog. Inside this gallery/playlist, you can add your favorite youtube videos or any video that you want to share with your visitors. Because of its default dimensions (width of 765px), it is recommended to add it just below the blog header, however, you can edit CSS code to fit anywhere you want to add it.

youtube playlist for blogger


To add this video slider/youtube playlist to your blog, please follow these steps below:

Step 1. From your blogger dashboard, go to Template > Edit HTML and click anywhere inside the code area to open the Blogger search box by pressing the CTRL + F keys.


Step 2. Type or paste the </head> tag inside the search box and hit Enter to find it.
Just above this tag, add the following scripts:

<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js' type='text/javascript'/>
<script>
//<![CDATA[
/*hoverscroll v.0.2.4*/
(function($) {
$.fn.hoverscroll = function(params) {
if (!params) { params = {}; }
params = $.extend({}, $.fn.hoverscroll.params, params);
this.each(function() {
var $this = $(this);
if (params.debug) {$.log('[HoverScroll] Trying to create hoverscroll on element ' + this.tagName + '#' + this.id);}
if (params.fixedArrows) {
$this.wrap('<div class="fixed-listcontainer"></div>')
}
else {
$this.wrap('<div class="listcontainer"></div>');
}

$this.addClass('list');
var listctnr = $this.parent();
listctnr.wrap('<div class="ui-widget-content hoverscroll' +
(params.rtl && !params.vertical ? " rtl" : "") + '"></div>');
//listctnr.wrap('<div class="hoverscroll"></div>');

var ctnr = listctnr.parent();

var leftArrow, rightArrow, topArrow, bottomArrow;
if (params.arrows) {
if (!params.vertical) {
if (params.fixedArrows) {
leftArrow = '<div class="fixed-arrow left"></div>';
rightArrow = '<div class="fixed-arrow right"></div>';

listctnr.before(leftArrow).after(rightArrow);
}
else {
leftArrow = '<div class="arrow left"></div>';
rightArrow = '<div class="arrow right"></div>';

listctnr.append(leftArrow).append(rightArrow);
}
}
else {
if (params.fixedArrows) {
topArrow = '<div class="fixed-arrow top"></div>';
bottomArrow = '<div class="fixed-arrow bottom"></div>';

listctnr.before(topArrow).after(bottomArrow);
}
else {
topArrow = '<div class="arrow top"></div>';
bottomArrow = '<div class="arrow bottom"></div>';

listctnr.append(topArrow).append(bottomArrow);
}
}
}
ctnr.width(params.width).height(params.height);

if (params.arrows && params.fixedArrows) {
if (params.vertical) {
topArrow = listctnr.prev();
bottomArrow = listctnr.next();

listctnr.width(params.width)
.height(params.height - (topArrow.height() + bottomArrow.height()));
}
else {
leftArrow = listctnr.prev();
rightArrow = listctnr.next();

listctnr.height(params.height)
.width(params.width - (leftArrow.width() + rightArrow.width()));
}
}
else {
listctnr.width(params.width).height(params.height);
}

var size = 0;

if (!params.vertical) {
ctnr.addClass('horizontal');
$this.children().each(function() {
$(this).addClass('item');

if ($(this).outerWidth) {
size += $(this).outerWidth(true);
}
else {
size += $(this).width() + parseInt($(this).css('padding-left')) + parseInt($(this).css('padding-right'))
+ parseInt($(this).css('margin-left')) + parseInt($(this).css('margin-right'));
}
});
$this.width(size);

if (params.debug) {
$.log('[HoverScroll] Computed content width : ' + size + 'px');
}
if (ctnr.outerWidth) {
size = ctnr.outerWidth();
}
else {
size = ctnr.width() + parseInt(ctnr.css('padding-left')) + parseInt(ctnr.css('padding-right'))
+ parseInt(ctnr.css('margin-left')) + parseInt(ctnr.css('margin-right'));
}

if (params.debug) {
$.log('[HoverScroll] Computed container width : ' + size + 'px');
}
}
else {
ctnr.addClass('vertical');
$this.children().each(function() {
$(this).addClass('item')

if ($(this).outerHeight) {
size += $(this).outerHeight(true);
}
else {
size += $(this).height() + parseInt($(this).css('padding-top')) + parseInt($(this).css('padding-bottom'))
+ parseInt($(this).css('margin-bottom')) + parseInt($(this).css('margin-bottom'));
}
});
$this.height(size);

if (params.debug) {
$.log('[HoverScroll] Computed content height : ' + size + 'px');
}
if (ctnr.outerHeight) {
size = ctnr.outerHeight();
}
else {
size = ctnr.height() + parseInt(ctnr.css('padding-top')) + parseInt(ctnr.css('padding-bottom'))
+ parseInt(ctnr.css('margin-top')) + parseInt(ctnr.css('margin-bottom'));
}

if (params.debug) {
$.log('[HoverScroll] Computed container height : ' + size + 'px');
}
}
var zone = {
1: {action: 'move', from: 0, to: 0.06 * size, direction: -1 , speed: 16},
2: {action: 'move', from: 0.06 * size, to: 0.15 * size, direction: -1 , speed: 8},
3: {action: 'move', from: 0.15 * size, to: 0.25 * size, direction: -1 , speed: 4},
4: {action: 'move', from: 0.25 * size, to: 0.4 * size, direction: -1 , speed: 2},
5: {action: 'stop', from: 0.4 * size, to: 0.6 * size},
6: {action: 'move', from: 0.6 * size, to: 0.75 * size, direction: 1 , speed: 2},
7: {action: 'move', from: 0.75 * size, to: 0.85 * size, direction: 1 , speed: 4},
8: {action: 'move', from: 0.85 * size, to: 0.94 * size, direction: 1 , speed: 8},
9: {action: 'move', from: 0.94 * size, to: size, direction: 1 , speed: 16}
}

ctnr[0].isChanging = false;
ctnr[0].direction = 0;
ctnr[0].speed = 1;
function checkMouse(x, y) {
x = x - ctnr.offset().left;
y = y - ctnr.offset().top;

var pos;
if (!params.vertical) {pos = x;}
else {pos = y;}

for (i in zone) {
if (pos >= zone[i].from && pos < zone[i].to) {
if (zone[i].action == 'move') {startMoving(zone[i].direction, zone[i].speed);}
else {stopMoving();}
}
}
}

function setArrowOpacity() {
if (!params.arrows || params.fixedArrows) {return;}

var maxScroll;
var scroll;

if (!params.vertical) {
maxScroll = listctnr[0].scrollWidth - listctnr.width();
scroll = listctnr[0].scrollLeft;
}
else {
maxScroll = listctnr[0].scrollHeight - listctnr.height();
scroll = listctnr[0].scrollTop;
}
var limit = params.arrowsOpacity;
var opacity = (scroll / maxScroll) * limit;

if (opacity > limit) { opacity = limit; }
if (isNaN(opacity)) { opacity = 0; }

var done = false;
if (opacity <= 0) {
$('div.arrow.left, div.arrow.top', ctnr).hide();
if(maxScroll > 0) {
$('div.arrow.right, div.arrow.bottom', ctnr).show().css('opacity', limit);
}
done = true;
}
if (opacity >= limit || maxScroll <= 0) {
$('div.arrow.right, div.arrow.bottom', ctnr).hide();
done = true;
}

if (!done) {
$('div.arrow.left, div.arrow.top', ctnr).show().css('opacity', opacity);
$('div.arrow.right, div.arrow.bottom', ctnr).show().css('opacity', (limit - opacity));
}
}

function startMoving(direction, speed) {
if (ctnr[0].direction != direction) {
if (params.debug) {
$.log('[HoverScroll] Starting to move. direction: ' + direction + ', speed: ' + speed);
}

stopMoving();
ctnr[0].direction = direction;
ctnr[0].isChanging = true;
move();
}
if (ctnr[0].speed != speed) {
if (params.debug) {
$.log('[HoverScroll] Changed speed: ' + speed);
}

ctnr[0].speed = speed;
}
}

function stopMoving() {
if (ctnr[0].isChanging) {
if (params.debug) {
$.log('[HoverScroll] Stoped moving');
}

ctnr[0].isChanging = false;
ctnr[0].direction = 0;
ctnr[0].speed = 1;
clearTimeout(ctnr[0].timer);
}
}

function move() {
if (ctnr[0].isChanging == false) {return;}

setArrowOpacity();

var scrollSide;
if (!params.vertical) {scrollSide = 'scrollLeft';}
else {scrollSide = 'scrollTop';}

listctnr[0][scrollSide] += ctnr[0].direction * ctnr[0].speed;
ctnr[0].timer = setTimeout(function() {move();}, 50);
}

if (params.rtl && !params.vertical) {
listctnr[0].scrollLeft = listctnr[0].scrollWidth - listctnr.width();
}

ctnr
.mousemove(function(e) {checkMouse(e.pageX, e.pageY);})
.bind('mouseleave', function() {stopMoving();});

this.startMoving = startMoving;
this.stopMoving = stopMoving;

if (params.arrows && !params.fixedArrows) {
// Initialise arrow opacity
setArrowOpacity();
}
else {
// Hide arrows
$('.arrowleft, .arrowright, .arrowtop, .arrowbottom', ctnr).hide();
}
});

return this;
};

if (!$.fn.offset) {
$.fn.offset = function() {
this.left = this.top = 0;

if (this[0] && this[0].offsetParent) {
var obj = this[0];
do {
this.left += obj.offsetLeft;
this.top += obj.offsetTop;
} while (obj = obj.offsetParent);
}

return this;
}
}

$.fn.hoverscroll.params = {
vertical: false,
width: 400,
height: 50,
arrows: true,
arrowsOpacity: 0.7,
fixedArrows: false,
rtl: false,
debug: false
};
$.log = function() {
try {console.log.apply(console, arguments);}
catch (e) {
try {opera.postError.apply(opera, arguments);}
catch (e) {}
}
};
})(jQuery);

$(function(){
$("#videoslider-tabs a").click(function(){
var container = $("#videoslider-content");
container.html("<img src='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinNXJlbxsOsIjG03xkqYW76Lov06My8xDryFxNxkR_h9C_kA4vcYfJUtVYPzY0yFGTP8yFVj3Ify7SrAiX_PRBrqs2L11yGxnhwzkNKfBaLH6JvL80vfBbcA7jK674p-3yOek9TsTGIw9m/s1600/loading.png' class='loading-vid' />");
var id = $(this).attr("href").slice(1);
loadvideo(id);
return false;
});
$("#videoslider-tabs").hoverscroll({vertical:true,width:300,height:330,arrows:false});
$("#videoslider-tabs li").hover(function(){$(this).addClass("hover");},function(){$(this).removeClass("hover");});
loadvideo();
});

function loadvideo (hash){if(hash){hash = hash.slice(3);$("#videoslider-content").html(video[hash]);$("#videoslider-tabs li").removeClass("actVid");$("#videoslider-tabs a[href=#vid"+hash+"]").parent().addClass("actVid");}else{$("#videoslider-content").html(video[1]);$("#videoslider-tabs li").removeClass("actVid");$("#videoslider-tabs a[href=#vid1]").parent().addClass("actVid");}}
//]]>
</script>

Step 3. Now let's add the CSS code. Just above the same </head> tag, add this code:
<style>
#videoslider {
background:#F6F6F6; /* Background color */
clear:both;
margin:0 auto;
padding:5px;
width:735px;
border-radius: 5px;
border: 1px solid #C1C1C1;
-moz-background-clip: padding;
-webkit-background-clip: padding-box;
background-clip: padding-box;
}
#videoslider, #videoslider-content, #videoslider-tabs {
height:340px;
}
#videoslider-content {
color:#fff;
float:left;
text-align:center;
width:430px;
z-index:1;
}
.loading-vid {
display:block;
margin:165px auto 0;
}
#videoslider-tabs {
float:right;
width:240px;
margin:0;
}
#videoslider-tabs li {
background-color:#F6F6F6;
border: 1px solid #C1C1C1;
float:left;
height:60px;
padding:5px;
width:240px;
list-style:none;
overflow: hidden;
}
#videoslider-tabs li a {
padding:0 !important;
border:0 !important;
}
#videoslider-tabs li.hover {
background-color:#EBEBEB;
}
#videoslider-tabs li.actVid {
background:#555;
}
#videoslider-tabs li img.thumb-vid {
background-color:#EEEEEE;
float:left;
height:52px;
margin:0 8px 0 0;
padding:5px;
width:52px;
}
#videoslider-tabs li span.vidTit {
display:block;
color:#128EC9; /* Titles color */
font-size:12px; /* Titles font size */
font-weight:bold;
text-decoration:none;
}
#videoslider-tabs li .vidDesc {
display:block;
color:#222222; /* Color of the description */
font-size:11px; /* Font size of the description */
font-weight:bold;
text-decoration:none;
}
#videoslider-tabs a {
text-decoration:none;
}
#videoslider-tabs li.actVid .vidDesc {
color:#fff;
}
#videoslider-tabs li span.vidTit .actVid {
color:#fff; /* Titles color */
}
.ui-widget-content{
float:right;
overflow-y: scroll;
overflow-x: hidden;
}
.tabs-outer {
background-image: none !important;
}
#videoslider-tabs li a:hover {
background: none !important;
}</style>
Step 4. Click on the Save Template button

To make this youtube playlist work we need to add the HTML structure of it:

Step 5. Go to Layout or Pages or anywhere you want to add it and add the following code inside the HTML area (if you want to add it as a widget, paste the code inside a HTML/Javascript gadget by clicking on Add a gadget link within the Layout/Page Elements section):
<script>
var video = [];
video[1] = '<iframe width="460" height="345" src="http://www.youtube.com/embed/video_ID_1" frameborder="0" allowfullscreen></iframe>';
video[2] = '<iframe width="460" height="345" src="http://www.youtube.com/embed/video_ID_2" frameborder="0" allowfullscreen></iframe>';
video[3] = '<iframe width="460" height="345" src="http://www.youtube.com/embed/video_ID_3" frameborder="0" allowfullscreen></iframe>';
video[4] = '<iframe width="460" height="345" src="http://www.youtube.com/embed/video_ID_4" frameborder="0" allowfullscreen></iframe>';
video[5] = '<iframe width="460" height="345" src="http://www.youtube.com/embed/video_ID_5" frameborder="0" allowfullscreen></iframe>';
</script>
<div id="videoslider">
<div id="videoslider-content"></div>
<ul id="videoslider-tabs">
<li><a href="#vid1"><img src="//i2.ytimg.com/vi/video_ID_1/default.jpg" class="thumb-vid" /><span class="vidTit">Video Name</span><span class="vidDesc">Video Description</span></a></li>

<li><a href="#vid2"><img src="//i2.ytimg.com/vi/video_ID_2/default.jpg" class="thumb-vid" /><span class="vidTit">Video Name</span><span class="vidDesc">Video Description</span></a></li>

<li><a href="#vid3"><img src="//i2.ytimg.com/vi/video_ID_3/default.jpg" class="thumb-vid" /><span class="vidTit">Video Name</span><span class="vidDesc">Video Description</span></a></li>

<li><a href="#vid4"><img src="//i2.ytimg.com/vi/video_ID_4/default.jpg" class="thumb-vid" /><span class="vidTit">Video Name</span><span class="vidDesc">Video Description</span></a></li>

<li><a href="#vid5"><img src="//i2.ytimg.com/vi/video_ID_5/default.jpg" class="thumb-vid" /><span class="vidTit">Video Name</span><span class="vidDesc">Video Description</span></a></li>
</ul>
</div>

Add the IDs of your videos to where it says video_ID_..., titles (Video Name) and a brief description (Description). To get the ID of a video, open the video on YouTube and look at the address bar - you need only the last characters after "v="


Note that each video ID should be added two times, the ones in red are for the video and the ones in orange are for the tabs thumbnail.

To add more videos, add two lines like the ones in bold above and then change the video[] and #vid... number. If you decided to add it inside a post or page, please make sure that you don't switch back to the Compose tab and publish your post while you're still on the HTML tab, otherwise the #vid location will be replaced with some other odd Blogger URLs.

Thursday, 12 September 2013

Advanced image optimization: Exif matters!

You want surely , that your site comes into relevant SERP with its images. There are some technics, which will enrich your images and let them be better indexed. Beside of this we talk about image size optimization - size matters, you know.
Read full article »

How to automatically share every Blogger post you publish on your Google+ page or profile

This quick-tip is about Google's new feature for automagically sharing every Blogger post to Google+



This post explains how to do it:



(How did I do that? Using Google+'s new embed feature. Do you like it? Should I make more quick-tips like this?)



Something I haven't been able to figure out yet is whether this happens for all posts (including edits of existing posts) or just for newly published posts. If it's the former, then getting your posts right before you publish them is probably more important than ever.

Wednesday, 11 September 2013

Malware: the blame game



As you may know, there's a never-ending debate between who's at fault when a user is infected:
  •  is it the user for being "gullable" or being social engineered to click on a malicious link?
  •  is it the fault of the antivirus or antimalware application for missing an infection?
  •  is it the fault of the administrator in corporate networks for not having proper policies?
  •  last but not least side-question: is antivirus useless?

Here's an excellent article which goes deeper into these questions and discusses about it:
http://www.welivesecurity.com/2013/01/03/imperva-virustotal-and-whether-av-is-useful/
(TL;DR: Imperva performed an antivirus test with doubtful and possibly improper testing methods and the (antivirus) community reacted on it)

My personal opinion? There's only one group to blame here which seems to get missed in these debates: the malware writers themselves. After all, the people who create (and use) the malware are responsible for the millions of infected machines and affected businesses, which may both lose a considerable amount of money by either
  • users: paying up to ransomware or rogueware, or CC (Credit Card) theft or fraud
  • businesses: personal records stolen (user/password databases), business plans stolen, not to mention the financial & productional losses.

So what's the endless discussion about and why are we not blaming the malware authors and botnet operators? (to learn more about botnets see my blogpost: the botnet wars: a Q&A)

Here are the main points antivirus companies are blamed on:
  • making money on the back of the customer and 
  • not protecting well enough.  

How much of this is true? Is antivirus dead? My only comment about this:
antivirus provides a good (basic) layer or level of protection on your machine. Is it sufficient? Maybe. Do you need extra protection? Depends. If you're a normal "home user", an antivirus and firewall will surely suffice. Free or paid antivirus doesn't really matter at that point. If you're in an organisation or corporation, antivirus will surely provide a good base to start from, not only signature-based but heuristically as well.

But you'll need more. Ideally, you need an extra set of eyes just for monitoring unusual behavior in your network. Is this realistic? Maybe. Are there solutions specifically designed for this on the market? Yes.

I won't go any deeper into the points above, as it's been discussed & debated upon many times.

Moving on:

Do ISPs (Internet Service Provider) need to take an arrow in the knee for this? How many and which ISPs are already detecting machines which are infected? These are newer and interesting questions as well. ISPs are obviously not responsible when a user is getting infected, however... When that machine in question starts sending out quite a lot of traffic (zombie), does the ISP need to take action?

In my opinion, if there's indeed an unusual load of traffic coming from a machine (sending out mass emails, trying to DDoS a box, ...) the ISP should indeed warn the user.

Some ISPs already do this, for example:
CenturyLink, KPN, Time Warner, Xs4All, Ziggo, ...

Getting back to my original point. Whenever there's a big "outbreak" of malware or there's a so called "APT" (Advanced Persistent Threat) found, people from several branches of the industry are very fast to point fingers or play the blame game (hence the title of this post). Examples:


  • You have no proper security implementations!
  • Your $securitysolution sucks! (use ours!)
  • You(r employees) are easily fooled!
  • You use Windows!
  • ...


It so appears that every single person is forgetting the simple fact that malware writers are actually the cause of one's computer issues. Not antivirus. Not Microsoft. Not the user. Not the ISP.
You can basically view these as buffers. Buffers against the malware. Buffers against the bad guys. Yes, you reading this now, you're actually a buffer as well! Do you have any idea on how often companies are suffering from attacks? How many attacks are actually prevented by $securitysolution, sysadmins and even users?

So, let's state it clear for once and for all. There's only one entity to blame:
the malware writer / botnet operator / put-other-synonym-for-bad-guy-here

Why am I using the word "entity" you may wonder? Well... You must know that malware writer and botnet operator aren't actually synonyms (as opposed to suggested above). The malware writer isn't necessary a botnet operator or the other way around. One thing's for sure though: they both take the blame here.

The malware writer for creating and distributing the malware in the first place.
The botnet operator or herder for consequently infecting users.

Here's a simple flowchart I made about how the current "blame" situation is:
(the direction of the arrow indicates who is blaming who)

Note: may differ from current view


An ideal flowchart would be:


An ideal world?

























I propose a new model. One where nobody gets the blame, except for the malware writer malicious entity.


A model where nobody points the finger to the user, which seems to happen in quite a lot of the cases. 

Indeed, a joint effort is necessary in this particular subject. It requires effort from all the involved parties. 


We'll start with each and go build our foundation, our basis:


The user:

  • Should know his or her responsibility and consequences when browsing the web
  • Should install an antivirus & firewall (free or not is irrelevant, as long as both elements are present)
  • Should know there's no 100% protection. There's a maximum of 99(,9?)% protection at least.
  • ... That's basically it.

The antivirus vendor:

  • Should acknowledge the user.
  • Should know the user's needs and shortcomings
  • Should know there's no 100% protection. There's a maximum of 99(,9?)% protection at least.
  • ... That's basically it.


The security company:

  • Should acknowledge both the user and the antivirus vendor
  • Should keep giving feedback for both instances
  • Should acknowledge the cat and mouse game between "viruses" and "antiviruses"
  • ... That's basically it.


Microsoft:

  • See The antivirus vendor and The security company


The 3rd party app:

  • Should acknowledge the user
  • Should know the user's needs and shortcomings and therefore:
  • Simplify the processes while increasing the security (not easy, I know)

That's basically it. If by now you're still thinking things like "users are gullible", "X antivirus is really bad", "Y security company is really lacking", "Windows is filled with vulnerabilities", "Java, Adobe, etc. are so easily exploited", .... Then you missed the point of this post. Start again from the top.

The foundations suggested above are what they are, foundations, and is how I see it. Your foundations may differ depending on the situation you're in, but in the end we're all in the same situation:

"fighting the malicious entity".


That is why there's a need for cooperation, coordination. There are countless possibilities, but to give a few examples for a kick start (for once let's get a step ahead of the bad guys):

The 3rd party app:

Not too many options here, besides:
  • listening to feedback from security companies and researchers and
  • prioritize security and provide sufficient information about security patches.

Microsoft:
  • Continue the cooperation that currently exists between security companies and others
  • Share your research, especially new malware trends. Everyone benefits!

The security company:
  • Continue the cooperation that may currently exist between you and other companies
  • Found anything interesting? Don't hesitate to share. 

Note: I realize there are sometimes reasons specific findings or research may not or cannot be shared. Obviously these specific situations should not be shared then. If you're in this industry, I'm sure you'll know why. An alternative some companies are applying is simply not naming who or what has been affected, but still outlining the incident, solution approach and solution on itself.


The antivirus vendor:
  • Consolidate your resources. There are countless researchers out there who are simply eager to share their findings, suggestions, research or simple MD5 hashes with you
  • Share your own findings as well when there's an "APT". Do not simply use it for the next big marketing move
  • Share, where appropriate, MD5 hashes so the community can benefit.

The ISP:
  • Warn your customers when you see an unusual and/or malicious high traffic load from end-users

The webhost or hosting provider:
  • Provide clear, useful and enough information on how to send an abuse report

Note: I realize there are more than enough (malicious) webhosts out there which do not list an abuse@ address, provide a fake one or do simply not reply. If you are a webhost, start implementing proper security checks so there's no malware being hosted on one of the websites you provide. Provide an email address or online form where security companies and/or researchers and users can send their abuse reports.


Last, but not least:

Users:
  • Don't panic. Panic is a bad counselor. Stay focused and note down what happened or at least what you noticed or think what happened. What did you do right before the culprit happened?
    Did it turn out your version of Office or Windows is illegal?
    Did you click on a link? Did you pick up a call from "Microsoft Support" but ended up in paying countless dollars/euros/pounds/etc. for a problem that didn't even exist in the first place? 
  • Have you been infected with malware (in particular banking malware or ransomware)? 
  • Were you the victim of CC theft, identity theft or any other form of online fraud or theft? 

Report it to the correct instances. Sadly, I found very little useful websites in regards to those situations. Prevention tips are scattered everywhere, but what to do afterwards, when you sit there and think about what has happened, well, that information is very scarce. What I did find is listed here:


Is this of no useful information to you? Exactly. More resources should be available for this.
"What now?":

  • Contact your local police office and file a "cybercrime" complaint: you're a victim!
  • Consult the website of your local CERT - Computer Emergency Response Team - Often they have additional information or may even have a hotline or contact form to report your incident.
  • ...




Conclusion

In this post I have addressed the current situation in regards of a malware infection and its results. Who is to blame? The answer is simple: the malicious entity. This may sound mysterious but as indicated above, I mean the malware writer and/or botnet operator. You can also call it the "cybercrook" or "cybercriminal" or whatever term best suits your needs.

I have proposed a new scheme, a new situation, a new model where we can all benefit from. Insights have been given and hopefully something can come out of it. As a matter of fact, it all boils down to these 3 points:


  • You are not to blame, only the malicious entity is to blame;
  • Look at yourself before pointing the finger to others who have in fact provided you all these years with resources!
  • Work together. Cooperate. Coordinate. Consolidate. You may call it "the 3 C's".
    Be victorious in your efforts to stop "cybercrime" once and for all!


Originally I had named this blogpost "Responsibility with malware infections", but as the post (yes, you may call it a rant if you like) continued to grow, I realised the current title fits the subject in a more appropriate and understandable way. Though you should still take your responsibilities when this kind of incident happens.


Questions? Comments? Feedback? Suggestions? I'm all open for it. Give me a shout-out on Twitter or simply post a comment below. I'll try to answer as soon as possible.