Keep sharp edges when using subdivision surface
You have two options:

Add supporting geometry (Loopcuts CtrlR and beveling CtrlB both work well):

The Subsurf modifier demonstrates why good, clean topology is so
important. As you can see in the figure, the Subsurf modifier has a
drastic effect on a default Cube. Until you add in additional Loops
(with CtrlR), the shape is almost unrecognizable. A mesh with a
deliberate topology has good placement of Edge Loops, which allow the
placement of more Loops (or removal of Loops, with X » Edge Loop) to
control the sharpness/smoothness of the resultant mesh.





Use Creases (ShiftE):

Weighted edge creases for subdivision surfaces allows you to change
the way Subsurf subdivides the geometry to give the edges a smooth or
sharp appearance.
The crease weight of selected edges can be changed using Transform
Properties (N) and change the Median Transform slider. A higher value
makes the edge “stronger” and more resistant to subsurf. Another way
to remember it is that the weight refers to the edge’s sharpness.
Edges with a higher weight will be deformed less by subsurf. Recall
that the subsurfed shape is a product of all intersecting edges, so to
make the edges of an area sharper, you have to increase the weight of
all the surrounding edges.




Creases tend to produce a sharp, hard edge which tends to look less realistic (and also tends to require an Edge Split modifier if used with smooth shading), while supporting geometry tends to give a subtly rounded edge which looks more realistic and does not require an edge split modifier. However, using supporting edge loops also tends to add a lot of extra geometry.