Very good question!
First, the explanation. When TikZ/PGF creates a node, then it stores various associated locations as "anchors". These can be used to refer to points on the node other than its main coordinate (its centre). A very common family of anchors is the border of the node, these can be referred to by angle, as in (node.45) or by compass direction, as in (node.north west). Although the node can define rules as it likes for these, the basic nodes tend to position these so that they are the sensible choice of point on the boundary of the node (an example of "sensible": on a rectangular node (node.north west) is always a corner).
The key phrase here is "on the boundary of the node". The line width is crucial here. If you write:
\node[red,draw, minimum size=2cm] (a) {};
\draw[<-] (a) -- ++(5,0);
then you want the arrow to stop at the edge of the boundary, not the middle of the boundary. If we make the line width ridiculous, then we can see the effect:

The arrow stops at the edge of the node boundary, not at the mid-point. To get the mid-point, we have to lengthen the line by half the line width. However, that's not a good solution since we need to use the line width that was in effect at the time that the node was drawn (which might not be the current line width) and it only works if the line is orthogonal to the boundary, otherwise we need to adjust the offset.
Better would be if the node itself knew of this "mid-boundary". There are two ways to do this that I can think of straight away. One is to define a new node shape where the anchor boundary was not adjusted by the line width. This would be fairly easy to do: copy the definition from the right file and make the necessary adjustments, but isn't something that I'd recommend for a one-shot picture or to someone not happy with \makeatletter ... \makeatother. If this is something that you're likely to use a lot then say so, and I'll help make the modifications if you need help.
A quicker (and dirtier) way of doing this is to separate the drawing of the border from its use as a way of setting the anchors. Two ways to do this spring to mind: use two nodes, or use one with a post- or pre-action. In the first case, you define a node which is not drawn but which defines the shape. Then you put on top a new node which is drawn. Then you refer to the first for the anchors. The second case is in effect the same, except that you combine the commands into one which avoids creating extra objects and unnecessary duplication of commands.
Here's the above example done the first way:
\node[minimum size=2cm,postaction={draw,thick,red},line width=0cm] (a) {};
\draw[<-] (a) -- ++(5,0);
We do have to specify the line width on the action, even if it is a pre-action, since the line width=0cm gets set immediately. It would be possible to define a way of saving the current line width and restoring it within the action.
Here's the same example with two nodes:
\node[minimum size=2cm,line width=0cm] (a) {};
\node[draw,red,minimum size=2cm] at (a) {};
\draw[<-] (a) -- ++(5,0);
The important thing with this one is to ensure that any size commands on the first are replicated. What would get difficult with this would be if the size of the node was determined by its contents.
Both of these produce the following:

Now we can see that the arrow goes where it should.
If we add this to your code, we can see its effect. To emphasis the effect, I've increased the line width to 1mm and drawn the dashed lines as solid red.

(Notice another effect of this shifting of anchors. Since the righthand square is positioned relative to the first, changing the node boundary changes its position ever-so slightly.)
To make it easier to code, I did what I suggested above and defined some keys. Thus the node itself is defined as:
\node [draw with anchor in boundary, minimum size=1cm] (a) {A};
The full code (for the above with both versions) is:
\documentclass[a4paper, 11pt]{article}
%\url{http://tex.stackexchange.com/q/29874/86}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows}
\usetikzlibrary{positioning}
\newdimen\zerolinewidth
\tikzset{
zero line width/.code={%
\zerolinewidth=\pgflinewidth
\tikzset{line width=0cm}%
},
use line width/.code={%
\tikzset{line width=\the\zerolinewidth}%
},
draw with anchor in boundary/.style={
zero line width,
postaction={draw,use line width},
},
}
\begin{document}
\begin{tikzpicture}[line width=1mm]
\node [draw, minimum size=1cm] (a) {A};
\node [draw, minimum size=3cm, right=2cm of a] (b) {B};
\draw [red] (a.north west) -- (b.north west);
\draw [red] (a.north east) -- (b.north east);
\draw [red] (a.south west) -- (b.south west);
\draw [red] (a.south east) -- (b.south east);
\end{tikzpicture}
\begin{tikzpicture}[line width=1mm]
\node [draw with anchor in boundary, minimum size=1cm] (a) {A};
\node [draw with anchor in boundary, minimum size=3cm, right=2cm of a] (b) {B};
\draw [red] (a.north west) -- (b.north west);
\draw [red] (a.north east) -- (b.north east);
\draw [red] (a.south west) -- (b.south west);
\draw [red] (a.south east) -- (b.south east);
\end{tikzpicture}
\end{document}
Update: The choice of preaction or postaction was initially arbitrary, but having seen the updated question I realise that I chose the wrong one. It should (as it now is) be a postaction. This is so that if the node is filled (which can be done on the main node as it isn't affected by line width) and then drawn then the draw should be done second so that it is on top of the fill. Otherwise the fill takes out half of the line width (as seen in the updated question).
semithick(my default setting) but TikZ default thickness which isthin. Since we're explicitly dealing withline widthhere, how do I set it to be semithick? – gablin Sep 29 '11 at 08:39semithickoption before thedraw with anchor in boundaryoption. That seems to work. Thedraw with anchor in boundarysaves the line width that is in effect at the time that it is used. Ensuring that it worked with setting the line width afterwards would be possible, but would involve Deep Hackery. – Andrew Stacey Sep 29 '11 at 09:34filloption. Please see my updated question. – gablin Sep 29 '11 at 10:21fills should always be done beforedraws. Since I was using apreaction, the draw was happening before the fill. I've changed it to apostactionwhich corrects this. Hopefully that now fixes it (let me know!). – Andrew Stacey Sep 29 '11 at 10:42